summaryrefslogtreecommitdiff
path: root/gcc/cp/decl.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/cp/decl.c')
-rw-r--r--gcc/cp/decl.c79
1 files changed, 62 insertions, 17 deletions
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 3aaa20c0ed3..73cf2bc58f7 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -92,7 +92,7 @@ static void layout_var_decl (tree);
static tree check_initializer (tree, tree, int, tree *);
static void make_rtl_for_nonlocal_decl (tree, tree, const char *);
static void save_function_data (tree);
-static void check_function_type (tree, tree);
+static bool check_function_type (tree, tree);
static void finish_constructor_body (void);
static void begin_destructor_body (void);
static void finish_destructor_body (void);
@@ -847,9 +847,23 @@ wrapup_globals_for_namespace (tree name_space, void* data)
struct cp_binding_level *level = NAMESPACE_LEVEL (name_space);
VEC(tree,gc) *statics = level->static_decls;
tree *vec = VEC_address (tree, statics);
+ tree decl;
int len = VEC_length (tree, statics);
int last_time = (data != 0);
+ /* Check undefined IFUNC symbols. */
+ for (decl = level->names; decl; decl = TREE_CHAIN (decl))
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_INITIAL (decl) == 0
+ && DECL_EXTERNAL (decl)
+ && ! TREE_NO_WARNING (decl)
+ && DECL_IS_IFUNC (decl))
+ {
+ error_at (input_location,
+ "indirect function %q+F never defined", decl);
+ TREE_NO_WARNING (decl) = 1;
+ }
+
if (last_time)
{
check_global_declarations (vec, len);
@@ -1646,6 +1660,25 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
error ("deleted definition of %qD", newdecl);
error ("after previous declaration %q+D", olddecl);
}
+
+ /* The IFUNC information must be on definition since it may
+ change the function return type inside the function body. */
+ if (new_defines_function
+ && DECL_IS_IFUNC (olddecl)
+ && !DECL_IS_IFUNC (newdecl))
+ {
+ error ("definition of function %q+D conflicts with",
+ newdecl);
+ error ("previous declaration of indirect function %q+D here",
+ olddecl);
+ return error_mark_node;
+ }
+
+ /* Merge the IFUNC information. */
+ if (DECL_IS_IFUNC (olddecl))
+ DECL_IS_IFUNC (newdecl) = 1;
+ else if (DECL_IS_IFUNC (newdecl))
+ DECL_IS_IFUNC (olddecl) = 1;
}
/* Deal with C++: must preserve virtual function table size. */
@@ -11781,9 +11814,10 @@ lookup_enumerator (tree enumtype, tree name)
}
-/* We're defining DECL. Make sure that its type is OK. */
+/* We're defining DECL. Make sure that its type is OK. Return TRUE
+ if its type is changed to void. */
-static void
+static bool
check_function_type (tree decl, tree current_function_parms)
{
tree fntype = TREE_TYPE (decl);
@@ -11797,7 +11831,7 @@ check_function_type (tree decl, tree current_function_parms)
validate_constexpr_fundecl (decl);
if (dependent_type_p (return_type))
- return;
+ return false;
if (!COMPLETE_OR_VOID_TYPE_P (return_type)
|| (TYPE_FOR_JAVA (return_type) && MAYBE_CLASS_TYPE_P (return_type)))
{
@@ -11821,9 +11855,13 @@ check_function_type (tree decl, tree current_function_parms)
fntype = (cp_build_type_attribute_variant
(fntype, TYPE_ATTRIBUTES (TREE_TYPE (decl))));
TREE_TYPE (decl) = fntype;
+ return true;
}
else
- abstract_virtuals_error (decl, TREE_TYPE (fntype));
+ {
+ abstract_virtuals_error (decl, TREE_TYPE (fntype));
+ return false;
+ }
}
/* Create the FUNCTION_DECL for a function definition.
@@ -11966,11 +12004,14 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
/* Make sure the parameter and return types are reasonable. When
you declare a function, these types can be incomplete, but they
- must be complete when you define the function. */
- check_function_type (decl1, current_function_parms);
+ must be complete when you define the function.
- /* Build the return declaration for the function. */
- restype = TREE_TYPE (fntype);
+ Use function_return_type to build the return declaration for the
+ function if FNTYPE isn't changed to void. */
+ if (check_function_type (decl1, current_function_parms))
+ restype = TREE_TYPE (fntype);
+ else
+ restype = function_return_type (decl1);
if (DECL_RESULT (decl1) == NULL_TREE)
{
tree resdecl;
@@ -12591,7 +12632,7 @@ tree
finish_function (int flags)
{
tree fndecl = current_function_decl;
- tree fntype, ctype = NULL_TREE;
+ tree restype, ctype = NULL_TREE;
int inclass_inline = (flags & 2) != 0;
int nested;
@@ -12606,7 +12647,7 @@ finish_function (int flags)
record_key_method_defined (fndecl);
nested = function_depth > 1;
- fntype = TREE_TYPE (fndecl);
+ restype = function_return_type (fndecl);
/* TREE_READONLY (fndecl) = 1;
This caused &foo to be of type ptr-to-const-function
@@ -12700,7 +12741,7 @@ finish_function (int flags)
if (r != error_mark_node
/* This is only worth doing for fns that return in memory--and
simpler, since we don't have to worry about promoted modes. */
- && aggregate_value_p (TREE_TYPE (TREE_TYPE (fndecl)), fndecl)
+ && aggregate_value_p (restype, fndecl)
/* Only allow this for variables declared in the outer scope of
the function so we know that their lifetime always ends with a
return; see g++.dg/opt/nrv6.C. We could be more flexible if
@@ -12728,9 +12769,10 @@ finish_function (int flags)
save_function_data (fndecl);
/* Complain if there's just no return statement. */
- if (warn_return_type
- && TREE_CODE (TREE_TYPE (fntype)) != VOID_TYPE
- && !dependent_type_p (TREE_TYPE (fntype))
+ if ((warn_return_type
+ || DECL_IS_IFUNC (fndecl))
+ && TREE_CODE (restype) != VOID_TYPE
+ && !dependent_type_p (restype)
&& !current_function_returns_value && !current_function_returns_null
/* Don't complain if we abort or throw. */
&& !current_function_returns_abnormally
@@ -12742,8 +12784,11 @@ finish_function (int flags)
&& !DECL_CONSTRUCTOR_P (fndecl)
&& !DECL_DESTRUCTOR_P (fndecl))
{
- warning (OPT_Wreturn_type,
- "no return statement in function returning non-void");
+ if (DECL_IS_IFUNC (fndecl))
+ error ("control reaches end of indirect function");
+ else
+ warning (OPT_Wreturn_type,
+ "no return statement in function returning non-void");
TREE_NO_WARNING (fndecl) = 1;
}