aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2024-11-11 19:54:32 +0100
committerJakub Jelinek <jakub@gcc.gnu.org>2024-11-11 20:04:33 +0100
commit417b4cc9bf218083838aeab458bbb7510e36375a (patch)
tree0250e834233b8fdbf772d8cce01a1ff1ff32e24c /gcc/cp
parentdfc9062eca47c237953c88a5614ae792792d058d (diff)
downloadgcc-417b4cc9bf218083838aeab458bbb7510e36375a.zip
gcc-417b4cc9bf218083838aeab458bbb7510e36375a.tar.gz
gcc-417b4cc9bf218083838aeab458bbb7510e36375a.tar.bz2
c++: Add __builtin_operator_{new,delete} support
clang++ adds __builtin_operator_{new,delete} builtins which as documented work similarly to ::operator {new,delete}, except that it is an error if the called ::operator {new,delete} is not a replaceable global operator and allow optimizations which C++ normally allows just when those are used from new/delete expressions https://eel.is/c++draft/expr.new#14 When using these builtins, the same optimizations can be done even when using those builtins. For GCC we note that in the CALL_FROM_NEW_OR_DELETE_P flag on CALL_EXPRs. The following patch implements it as a C++ FE keyword (because passing references through ... changes the argument and so BUILT_IN_FRONTEND builtin can't be used), just attempts to call the ::operator {new,delete} and if it isn't replaceable, diagnoses it. libstdc++ already uses the builtin in some cases. 2024-11-11 Jakub Jelinek <jakub@redhat.com> gcc/c-family/ * c-common.h (enum rid): Add RID_BUILTIN_OPERATOR_NEW and RID_BUILTIN_OPERATOR_DELETE. (names_builtin_p): Change return type from bool to int. * c-common.cc (c_common_reswords): Add __builtin_operator_new and __builtin_operator_delete. gcc/c/ * c-decl.cc (names_builtin_p): Change return type from bool to int, adjust return statments. gcc/cp/ * parser.cc (cp_parser_postfix_expression): Handle RID_BUILTIN_OPERATOR_NEW and RID_BUILTIN_OPERATOR_DELETE. * cp-objcp-common.cc (names_builtin_p): Change return type from bool to int, adjust return statments. Handle RID_BUILTIN_OPERATOR_NEW and RID_BUILTIN_OPERATOR_DELETE. * pt.cc (tsubst_expr) <case CALL_EXPR>: Handle CALL_FROM_NEW_OR_DELETE_P. gcc/ * doc/extend.texi (New/Delete Builtins): Document __builtin_operator_new and __builtin_operator_delete. gcc/testsuite/ * g++.dg/ext/builtin-operator-new-1.C: New test. * g++.dg/ext/builtin-operator-new-2.C: New test. * g++.dg/ext/builtin-operator-new-3.C: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/cp-objcp-common.cc21
-rw-r--r--gcc/cp/parser.cc35
-rw-r--r--gcc/cp/pt.cc24
3 files changed, 71 insertions, 9 deletions
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 1e43db3..921931b 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -545,10 +545,10 @@ identifier_global_tag (tree name)
return ret;
}
-/* Returns true if NAME refers to a built-in function or function-like
- operator. */
+/* Returns non-zero (result of __has_builtin) if NAME refers to a built-in
+ function or function-like operator. */
-bool
+int
names_builtin_p (const char *name)
{
tree id = get_identifier (name);
@@ -556,23 +556,23 @@ names_builtin_p (const char *name)
{
if (TREE_CODE (binding) == FUNCTION_DECL
&& DECL_IS_UNDECLARED_BUILTIN (binding))
- return true;
+ return 1;
/* Handle the case when an overload for a built-in name exists. */
if (TREE_CODE (binding) != OVERLOAD)
- return false;
+ return 0;
for (ovl_iterator it (binding); it; ++it)
{
tree decl = *it;
if (DECL_IS_UNDECLARED_BUILTIN (decl))
- return true;
+ return 1;
}
}
/* Check for built-in traits. */
if (IDENTIFIER_TRAIT_P (id))
- return true;
+ return 1;
/* Also detect common reserved C++ words that aren't strictly built-in
functions. */
@@ -587,12 +587,15 @@ names_builtin_p (const char *name)
case RID_BUILTIN_ASSOC_BARRIER:
case RID_BUILTIN_BIT_CAST:
case RID_OFFSETOF:
- return true;
+ return 1;
+ case RID_BUILTIN_OPERATOR_NEW:
+ case RID_BUILTIN_OPERATOR_DELETE:
+ return 201802L;
default:
break;
}
- return false;
+ return 0;
}
/* Register c++-specific dumps. */
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index c1375ec..07b1222 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -7733,6 +7733,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
case RID_BUILTIN_SHUFFLEVECTOR:
case RID_BUILTIN_LAUNDER:
case RID_BUILTIN_ASSOC_BARRIER:
+ case RID_BUILTIN_OPERATOR_NEW:
+ case RID_BUILTIN_OPERATOR_DELETE:
{
vec<tree, va_gc> *vec;
@@ -7819,6 +7821,39 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
}
break;
+ case RID_BUILTIN_OPERATOR_NEW:
+ case RID_BUILTIN_OPERATOR_DELETE:
+ tree fn;
+ fn = ovl_op_identifier (keyword == RID_BUILTIN_OPERATOR_NEW
+ ? NEW_EXPR : DELETE_EXPR);
+ fn = lookup_qualified_name (global_namespace, fn);
+ postfix_expression = finish_call_expr (fn, &vec, true, false,
+ tf_warning_or_error);
+ if (postfix_expression != error_mark_node)
+ {
+ tree call = extract_call_expr (postfix_expression);
+ fn = cp_get_callee_fndecl_nofold (call);
+ if (fn ? !DECL_IS_REPLACEABLE_OPERATOR (fn)
+ : !processing_template_decl)
+ {
+ auto_diagnostic_group d;
+ if (keyword == RID_BUILTIN_OPERATOR_NEW)
+ error_at (loc, "call to %<__builtin_operator_new%> "
+ "does not select replaceable global "
+ "allocation function");
+ else
+ error_at (loc, "call to %<__builtin_operator_delete%> "
+ "does not select replaceable global "
+ "deallocation function");
+ if (fn)
+ inform (DECL_SOURCE_LOCATION (fn),
+ "selected function declared here");
+ }
+ else if (call && TREE_CODE (call) == CALL_EXPR)
+ CALL_FROM_NEW_OR_DELETE_P (call) = 1;
+ }
+ break;
+
default:
gcc_unreachable ();
}
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index f4213f8..35ec7eb 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -21242,6 +21242,30 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
else if (TREE_CODE (call) == AGGR_INIT_EXPR)
AGGR_INIT_EXPR_MUST_TAIL (call) = mtc;
}
+ if (CALL_FROM_NEW_OR_DELETE_P (t))
+ {
+ tree call = extract_call_expr (ret);
+ tree fn = cp_get_callee_fndecl_nofold (call);
+ if (fn ? !DECL_IS_REPLACEABLE_OPERATOR (fn)
+ : !processing_template_decl)
+ {
+ auto_diagnostic_group d;
+ location_t loc = cp_expr_loc_or_input_loc (t);
+ if (!fn || IDENTIFIER_NEW_OP_P (DECL_NAME (fn)))
+ error_at (loc, "call to %<__builtin_operator_new%> "
+ "does not select replaceable global "
+ "allocation function");
+ else
+ error_at (loc, "call to %<__builtin_operator_delete%> "
+ "does not select replaceable global "
+ "deallocation function");
+ if (fn)
+ inform (DECL_SOURCE_LOCATION (fn),
+ "selected function declared here");
+ }
+ else if (call && TREE_CODE (call) == CALL_EXPR)
+ CALL_FROM_NEW_OR_DELETE_P (call) = 1;
+ }
if (warning_suppressed_p (t, OPT_Wpessimizing_move))
/* This also suppresses -Wredundant-move. */
suppress_warning (ret, OPT_Wpessimizing_move);