Attached is a patch to reject C and C++ constructs that result
in obtaining a pointer (or a reference in C++) to a builtin
function. These constructs are currently silently accepted by
GCC and, in most cases(*), result in a linker error. The patch
brings GCC on par with Clang by rejecting all such constructs.
Bootstrapped and tested on x86_64-unknown-linux-gnu.
Okay to commit to trunk?
Martin
[*] Besides rejecting constructs that lead to linker errors
the patch leads to rejecting also some benign constructs (that
are also rejected by Clang). For example, the program below
currently compiles and links successfully with GCC 5.1 is
rejected with the patch applied. That's intended (but I'm
open to arguments against it).
$ cat /build/tmp/u.cpp && /build/gcc-66516/gcc/xg++
-B/build/gcc-66516/gcc -Wall -c -std=c++11 /build/tmp/u.cpp
static constexpr int (&r)(int) = __builtin_ffs;
int main () { return r (0); }
/build/tmp/u.cpp:1:34: error: invalid initialization of a reference with
a builtin function
static constexpr int (&r)(int) = __builtin_ffs;
^
2015-06-21 Martin Sebor <mse...@redhat.com>
PR c/66516
* c/c-typeck.c (default_function_array_conversion): Reject
converting a builtin function to a pointer.
(parser_build_unary_op): Reject taking the address of a builtin
function.
* cp/call.c (convert_like_real): Reject converting a builtin function
to a pointer.
(initialize_reference): Reject initializing a reference with a builtin
function.
* cp/typeck.c (cp_build_addr_expr_strict): Reject taking the address
of a builtin function.
(build_reinterpret_cast_1): Reject casting a builtin function to
a pointer.
(convert_for_initialization): Reject initializing a pointer with
the a builtin function.
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 636e0bb..637a292 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -58,6 +58,8 @@ along with GCC; see the file COPYING3. If not see
#include "cilk.h"
#include "gomp-constants.h"
+#include <stdlib.h>
+
/* Possible cases of implicit bad conversions. Used to select
diagnostic messages in convert_for_assignment. */
enum impl_conv {
@@ -1940,6 +1942,12 @@ default_function_array_conversion (location_t loc, struct c_expr exp)
}
break;
case FUNCTION_TYPE:
+ if (DECL_IS_BUILTIN (exp.value))
+ {
+ error_at (loc, "converting builtin function to a pointer");
+ exp.value = error_mark_node;
+ }
+ else
exp.value = function_to_pointer_conversion (loc, exp.value);
break;
default:
@@ -3384,7 +3392,14 @@ parser_build_unary_op (location_t loc, enum tree_code code, struct c_expr arg)
result.original_code = code;
result.original_type = NULL;
- if (TREE_OVERFLOW_P (result.value) && !TREE_OVERFLOW_P (arg.value))
+ if (code == ADDR_EXPR
+ && TREE_CODE (TREE_TYPE (arg.value)) == FUNCTION_TYPE
+ && DECL_IS_BUILTIN (arg.value))
+ {
+ error_at (loc, "taking address of a builtin function");
+ result.value = error_mark_node;
+ }
+ else if (TREE_OVERFLOW_P (result.value) && !TREE_OVERFLOW_P (arg.value))
overflow_warning (loc, result.value);
return result;
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 2d90ed9..df4cc77 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -6570,6 +6570,13 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
expr = build_target_expr_with_type (expr, type, complain);
}
+ if (TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE
+ && DECL_P (expr) && DECL_IS_BUILTIN (expr))
+ {
+ error_at (input_location, "taking address of a builtin function");
+ return error_mark_node;
+ }
+
/* Take the address of the thing to which we will bind the
reference. */
expr = cp_build_addr_expr (expr, complain);
@@ -9768,8 +9775,18 @@ initialize_reference (tree type, tree expr,
}
if (conv->kind == ck_ref_bind)
+ {
+ if (TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE
+ && DECL_P (expr) && DECL_IS_BUILTIN (expr))
+ {
+ error_at (input_location, "invalid initialization of a reference "
+ "with a builtin function");
+ return error_mark_node;
+ }
+
/* Perform the conversion. */
expr = convert_like (conv, expr, complain);
+ }
else if (conv->kind == ck_ambig)
/* We gave an error in build_user_type_conversion_1. */
expr = error_mark_node;
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 5b09b73..fbea052 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5621,6 +5621,13 @@ cp_build_addr_expr (tree arg, tsubst_flags_t complain)
static tree
cp_build_addr_expr_strict (tree arg, tsubst_flags_t complain)
{
+ if (TREE_CODE (TREE_TYPE (arg)) == FUNCTION_TYPE
+ && DECL_P (arg) && DECL_IS_BUILTIN (arg))
+ {
+ error_at (input_location, "taking address of a builtin function");
+ return error_mark_node;
+ }
+
return cp_build_addr_expr_1 (arg, 1, complain);
}
@@ -6860,6 +6867,14 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
|| VOID_TYPE_P (TREE_TYPE (type))))
return convert_member_func_to_ptr (type, expr, complain);
+ if (TREE_CODE (intype) == FUNCTION_TYPE
+ && DECL_P (expr) && DECL_IS_BUILTIN (expr))
+ {
+ error_at (EXPR_LOC_OR_LOC (expr, input_location),
+ "taking address of a builtin function");
+ return error_mark_node;
+ }
+
/* If the cast is not to a reference type, the lvalue-to-rvalue,
array-to-pointer, and function-to-pointer conversions are
performed. */
@@ -8307,6 +8322,13 @@ convert_for_initialization (tree exp, tree type, tree rhs, int flags,
|| (TREE_CODE (rhs) == TREE_LIST && TREE_VALUE (rhs) == error_mark_node))
return error_mark_node;
+ if (TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE
+ && DECL_P (rhs) && DECL_IS_BUILTIN (rhs))
+ {
+ error_at (input_location, "taking address of a builtin function");
+ return error_mark_node;
+ }
+
if ((TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE
&& TREE_CODE (type) != ARRAY_TYPE
&& (TREE_CODE (type) != REFERENCE_TYPE
2015-06-21 Martin Sebor <mse...@redhat.com>
PR c/66516
* gcc.dg/addr_builtin-pr66516.c: New test.
* g++.dg/addr_builtin-pr66516.C: New test.
diff --git a/gcc/testsuite/g++.dg/addr_builtin-pr66516.C b/gcc/testsuite/g++.dg/addr_builtin-pr66516.C
new file mode 100644
index 0000000..38f1a51
--- /dev/null
+++ b/gcc/testsuite/g++.dg/addr_builtin-pr66516.C
@@ -0,0 +1,70 @@
+/* { dg-do compile } */
+
+/* Verify that taking the address of a builtin function generates
+ a compilation error. */
+
+#if 201103L <= __cplusplus
+
+bool func (void)
+{
+ /* Taking the address of builtin constants is valid. */
+ return &__func__ == &__func__; /* { dg-bogus "builtin" } */
+}
+
+#endif
+
+/* Calling the builtins is valid. */
+int foo (int i)
+{
+ const void* p = 0;
+
+ switch (i & 0xf) {
+ case 0: i = __builtin_expect (i, 0); /* { dg-bogus "builtin" } */
+ case 1: p = __builtin_return_address (0); /* { dg-bogus "builtin" } */
+ case 2: i = __builtin_strlen (""); /* { dg-bogus "builtin" } */
+ case 3: p = __builtin_FILE (); /* { dg-bogus "builtin" } */
+ case 4: p = __builtin_FUNCTION (); /* { dg-bogus "builtin" } */
+ case 5: i = __builtin_LINE (); /* { dg-bogus "builtin" } */
+ }
+ return p ? *(int*)p : i;
+}
+
+void* const addr[] = {
+ (void*)__builtin_expect, /* { dg-error "builtin" } */
+ (void*)__builtin_return_address, /* { dg-error "builtin" } */
+ (void*)__atomic_load, /* { dg-error "builtin" } */
+ (void*)__builtin_add_overflow, /* { dg-error "builtin" } */
+ (void*)__builtin_constant_p, /* { dg-error "builtin" } */
+ (void*)__builtin_nanf, /* { dg-error "builtin" } */
+ (void*)__builtin_strlen, /* { dg-error "builtin" } */
+ (void*)__builtin_unreachable, /* { dg-error "builtin" } */
+ (void*)__builtin_FILE, /* { dg-error "builtin" } */
+ (void*)__builtin_FUNCTION, /* { dg-error "builtin" } */
+ (void*)__builtin_LINE, /* { dg-error "builtin" } */
+
+ (void*)&__builtin_expect, /* { dg-error "builtin" } */
+ (void*)&__builtin_return_address, /* { dg-error "builtin" } */
+ (void*)&__atomic_load, /* { dg-error "builtin" } */
+ (void*)&__builtin_add_overflow, /* { dg-error "builtin" } */
+ (void*)&__builtin_constant_p, /* { dg-error "builtin" } */
+ (void*)&__builtin_nanf, /* { dg-error "builtin" } */
+ (void*)&__builtin_strlen, /* { dg-error "builtin" } */
+ (void*)&__builtin_unreachable, /* { dg-error "builtin" } */
+ (void*)&__builtin_FILE, /* { dg-error "builtin" } */
+ (void*)&__builtin_FUNCTION, /* { dg-error "builtin" } */
+ (void*)&__builtin_LINE, /* { dg-error "builtin" } */
+
+ reinterpret_cast<void*>(__builtin_expect), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__builtin_return_address), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__atomic_load), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__builtin_add_overflow), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__builtin_constant_p), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__builtin_nanf), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__builtin_strlen), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__builtin_unreachable), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__builtin_FILE), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__builtin_FUNCTION), /* { dg-error "builtin" } */
+ reinterpret_cast<void*>(__builtin_LINE), /* { dg-error "builtin" } */
+
+ 0
+};
diff --git a/gcc/testsuite/gcc.dg/addr_builtin-pr66516.c b/gcc/testsuite/gcc.dg/addr_builtin-pr66516.c
new file mode 100644
index 0000000..b0b480e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/addr_builtin-pr66516.c
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+
+/* Verify that taking the address of a builtin function generates
+ a compilation error. */
+
+#if 199901L <= __STDC_VERSION__
+
+int func (void)
+{
+ /* Taking the address of builtin constants is valid. */
+ return &__func__ == &__func__; /* { dg-bogus "builtin" } */
+}
+
+#endif
+
+
+/* Calling the builtins is valid. */
+int foo (int i)
+{
+ const void* p = 0;
+
+ switch (i & 0xf) {
+ case 0: i = __builtin_expect (i, 0); /* { dg-bogus "builtin" } */
+ case 1: p = __builtin_return_address (0); /* { dg-bogus "builtin" } */
+ case 2: i = __builtin_strlen (""); /* { dg-bogus "builtin" } */
+ case 3: p = __builtin_FILE (); /* { dg-bogus "builtin" } */
+ case 4: p = __builtin_FUNCTION (); /* { dg-bogus "builtin" } */
+ case 5: i = __builtin_LINE (); /* { dg-bogus "builtin" } */
+ }
+ return p ? *(int*)p : i;
+}
+
+void* const addr[] = {
+ (void*)__builtin_expect, /* { dg-error "builtin" } */
+ (void*)__builtin_return_address, /* { dg-error "builtin" } */
+ (void*)__atomic_load, /* { dg-error "builtin" } */
+ (void*)__builtin_add_overflow, /* { dg-error "builtin" } */
+ (void*)__builtin_constant_p, /* { dg-error "builtin" } */
+ (void*)__builtin_nanf, /* { dg-error "builtin" } */
+ (void*)__builtin_strlen, /* { dg-error "builtin" } */
+ (void*)__builtin_unreachable, /* { dg-error "builtin" } */
+ (void*)__builtin_FILE, /* { dg-error "builtin" } */
+ (void*)__builtin_FUNCTION, /* { dg-error "builtin" } */
+ (void*)__builtin_LINE, /* { dg-error "builtin" } */
+
+ (void*)&__builtin_expect, /* { dg-error "builtin" } */
+ (void*)&__builtin_return_address, /* { dg-error "builtin" } */
+ (void*)&__atomic_load, /* { dg-error "builtin" } */
+ (void*)&__builtin_add_overflow, /* { dg-error "builtin" } */
+ (void*)&__builtin_constant_p, /* { dg-error "builtin" } */
+ (void*)&__builtin_nanf, /* { dg-error "builtin" } */
+ (void*)&__builtin_strlen, /* { dg-error "builtin" } */
+ (void*)&__builtin_unreachable, /* { dg-error "builtin" } */
+ (void*)&__builtin_FILE, /* { dg-error "builtin" } */
+ (void*)&__builtin_FUNCTION, /* { dg-error "builtin" } */
+ (void*)&__builtin_LINE, /* { dg-error "builtin" } */
+
+ 0
+};