===================================================================
@@ -288,6 +288,7 @@ Objective-C and Objective-C++ Dialects}.
-Wunused-label -Wunused-local-typedefs -Wunused-parameter @gol
-Wno-unused-result -Wunused-value @gol -Wunused-variable @gol
-Wunused-but-set-parameter -Wunused-but-set-variable @gol
+-Wupcast-result -Wupcast-var @gol
-Wuseless-cast -Wvariadic-macros -Wvector-operation-performance @gol
-Wvla -Wvolatile-register-var -Wwrite-strings @gol
-Wzero-as-null-pointer-constant}
@@ -4879,6 +4880,43 @@ compilations.
Warn when deleting a pointer to incomplete type, which may cause
undefined behavior at runtime. This warning is enabled by default.
+@item -Wupcast-result @r{(C++ and Objective-C++ only)}
+@opindex Wupcast-result
+@opindex Wno-upcast-result
+Warn about functions whose return type is more general than it needs
+to be. Specifically, if a function returns a pointer or reference
+to some base class @var{B} and all nonnull returns have an implicit
+upcast from a derived class, warn if there is a derived class @var{D}
+that would satisfy all such returns.
+
+The warning does not consider @var{D} to be a candidate if it is in an
+anonymous namespace or if it has the @code{no_upcast_warning} attribute.
+It also ignores returns from virtual functions, where general types are
+likely to be deliberate.
+
+For example, the warning would trigger on the following function,
+which could return @code{D1*} rather than @code{B*}:
+@cindex @code{no_upcast_warning} type attribute
+@smallexample
+struct B @{@};
+struct D1 : public B @{@};
+struct D2 : public D1 @{@};
+B *f(int x) @{ if (x) return new D1; else return new D2; @}
+@end smallexample
+
+@item -Wupcast-var @r{(C++ and Objective-C++ only)}
+@opindex Wupcast-var
+@opindex Wno-upcast-var
+Like @option{-Wupcast-result}, but warn about local variables that are
+more general than they need to be. For example, the warning would trigger
+on @code{x} in the following function, which could have type @code{D1*}
+rather than @code{B}:
+@smallexample
+struct B @{@};
+struct D1 : public B @{@};
+void f() @{ B *x = new D1; @}
+@end smallexample
+
@item -Wuseless-cast @r{(C++ and Objective-C++ only)}
@opindex Wuseless-cast
@opindex Wno-useless-cast
===================================================================
@@ -6179,6 +6179,19 @@ the case with lock or thread classes, wh
not referenced, but contain constructors and destructors that have
nontrivial bookkeeping functions.
+@item no_upcast_warning
+@cindex @code{no_upcast_warning} type attribute
+Specifies that a struct or class should be ignored by the C++ options
+@option{-Wupcast-result} and @option{-Wupcast-var} when searching for
+a more derived class. For example:
+@smallexample
+struct B @{@};
+struct __attribute__((no_upcast_warning)) D1 : public B @{@};
+B *f(int x) @{ return new D1; @}
+@end smallexample
+would not trigger a warning even when @option{-Wupcast-result} is
+in effect.
+
@item visibility
@cindex @code{visibility} type attribute
In C++, attribute visibility (@pxref{Function Attributes}) can also be
@@ -6192,7 +6205,6 @@ particular, if a class is thrown as an e
and caught in another, the class must have default visibility.
Otherwise the two shared objects are unable to use the same
typeinfo node and exception handling will break.
-
@end table
To specify multiple attributes, separate them by commas within the
===================================================================
@@ -896,6 +896,14 @@ Wunused-result
C ObjC C++ ObjC++ Var(warn_unused_result) Init(1) Warning
Warn if a caller of a function, marked with attribute warn_unused_result, does not use its return value
+Wupcast-result
+C++ ObjC++ Var(warn_upcast_result) Warning
+Warn when every nonnull return from a function performs an upcast from the same derived type to a base type.
+
+Wupcast-var
+C++ ObjC++ Var(warn_upcast_var) Warning
+Warn when every nonnull assignment to a variable performs an upcast from the same derived type to a base type.
+
Wvariadic-macros
C ObjC C++ ObjC++ CPP(warn_variadic_macros) CppReason(CPP_W_VARIADIC_MACROS) Var(cpp_warn_variadic_macros) Init(0) Warning LangEnabledBy(C ObjC C++ ObjC++,Wpedantic || Wtraditional)
Warn about using variadic macros
===================================================================
@@ -404,6 +404,8 @@ static tree handle_designated_init_attri
static tree handle_bnd_variable_size_attribute (tree *, tree, tree, int, bool *);
static tree handle_bnd_legacy (tree *, tree, tree, int, bool *);
static tree handle_bnd_instrument (tree *, tree, tree, int, bool *);
+static tree handle_no_upcast_warning_attribute (tree *, tree, tree, int,
+ bool *);
static void check_function_nonnull (tree, int, tree *);
static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT);
@@ -823,6 +825,8 @@ const struct attribute_spec c_common_att
handle_bnd_legacy, false },
{ "bnd_instrument", 0, 0, true, false, false,
handle_bnd_instrument, false },
+ { "no_upcast_warning", 0, 0, false, true, false,
+ handle_no_upcast_warning_attribute, false },
{ NULL, 0, 0, false, false, false, NULL, false }
};
@@ -8718,6 +8722,22 @@ handle_bnd_instrument (tree *node, tree
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+/* Handle a "no_upcast_warning" attribute; arguments as in
+ struct attribute_spec.handler. */
+
+static tree
+handle_no_upcast_warning_attribute (tree *node, tree name, tree, int,
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != RECORD_TYPE)
+ {
+ warning (OPT_Wattributes, "%qE attribute ignored", name);
+ *no_add_attrs = true;
}
return NULL_TREE;
===================================================================
@@ -1200,6 +1200,23 @@ struct named_label_hasher : ggc_hasher<n
static bool equal (named_label_entry *, named_label_entry *);
};
+/* Used to implement -Wupcast-var for VAR_DECL VAR, which has a pointer
+ or reference type. TARGET_TYPES is a list of types with which
+ TREE_TYPE (TREE_TYPE (VAR)) must be compatible. Each one is either
+ TREE_TYPE (TREE_TYPE (VAR)) or is derived from it. */
+
+struct GTY((for_user)) cp_decl_target_types
+{
+ tree var;
+ vec<tree, va_gc> *target_types;
+};
+
+struct cp_decl_target_types_hasher : ggc_hasher<cp_decl_target_types *>
+{
+ static hashval_t hash (cp_decl_target_types *);
+ static bool equal (cp_decl_target_types *, cp_decl_target_types *);
+};
+
/* Global state pertinent to the current function. */
struct GTY(()) language_function {
@@ -1232,6 +1249,8 @@ struct GTY(()) language_function {
/* Tracking possibly infinite loops. This is a vec<tree> only because
vec<bool> doesn't work with gtype. */
vec<tree, va_gc> *infinite_loops;
+ vec<tree, va_gc> *x_result_target_types;
+ hash_table<cp_decl_target_types_hasher> *x_decl_target_types;
hash_table<cxx_int_tree_map_hasher> *extern_decl_map;
};
@@ -1307,6 +1326,20 @@ #define in_function_try_handler cp_funct
#define current_function_return_value \
(cp_function_chain->x_return_value)
+/* Used to implement -Wupcast-result. If the current function returns a
+ pointer or reference, this is the list of types with which the
+ pointer or reference target (X) must be compatible. Each one is either
+ X or is derived from X. */
+
+#define current_function_result_target_types \
+ (cp_function_chain->x_result_target_types)
+
+/* Used to implement -Wupcast-var. It is a hash table of all variables
+ in the current function that might be relevant to the warning. */
+
+#define current_function_decl_target_types \
+ (cp_function_chain->x_decl_target_types)
+
/* A type involving 'auto' to be used for return type deduction. */
#define current_function_auto_return_pattern \
@@ -6241,6 +6274,7 @@ extern tree cp_build_c_cast (tree, tre
extern tree build_x_modify_expr (location_t, tree,
enum tree_code, tree,
tsubst_flags_t);
+extern void upcast_var_record_type (tree, tree);
extern tree cp_build_modify_expr (tree, enum tree_code, tree,
tsubst_flags_t);
extern tree convert_for_initialization (tree, tree, tree, int,
===================================================================
@@ -278,6 +278,76 @@ typedef struct GTY(()) incomplete_var_d
static GTY(()) vec<incomplete_var, va_gc> *incomplete_vars;
+/* On entry:
+
+ - Each element of TARGETS is, or is derived from, BASE.
+ - TARGETS[0] is, or is derived from, CANDIDATE; and
+ - CANDIDATE is, or is derived from, BASE
+
+ Find and return a CANDIDATE that satisfies the additional properties:
+
+ - Each element of TARGETS is, or is derived from, CANDIDATE
+ - CANDIDATE does not have the no_upcast_warning attribute
+
+ In hierarchies with no virtual bases, always return the most derived
+ such type (effectively the maximum lower bound). In hierarchies with
+ virtual bases the choice is more arbitrary, but is never BASE if an
+ alternative exists. */
+
+static tree
+get_common_target (vec<tree, va_gc> &targets, tree base, tree candidate)
+{
+ if (!lookup_attribute ("no_upcast_warning", TYPE_ATTRIBUTES (candidate)))
+ {
+ /* We already know CANDIDATE satisfies the condition for TARGETS[0]. */
+ unsigned int i;
+ for (i = 1; i < targets.length (); ++i)
+ {
+ tree other = targets[i];
+ if (candidate != other && !DERIVED_FROM_P (candidate, other))
+ break;
+ }
+ if (i == targets.length ())
+ return candidate;
+ }
+ tree binfo = TYPE_BINFO (candidate);
+ tree next_binfo;
+ for (unsigned int i = 0; BINFO_BASE_ITERATE (binfo, i, next_binfo); ++i)
+ {
+ tree next_try = BINFO_TYPE (next_binfo);
+ if (DERIVED_FROM_P (base, next_try))
+ {
+ tree res = get_common_target (targets, base, next_try);
+ if (res != base)
+ return res;
+ }
+ }
+ return base;
+}
+
+/* LHS_TYPE is a pointer or reference type and TARGETS is a list of
+ types that are, or are derived from, TREE_TYPE (LHS_TYPE). If the
+ same condition would hold for a derived class of TREE_TYPE (LHS_TYPE),
+ pick one such type -- as for get_common_target -- and return a pointer
+ or reference like LHS_TYPE that has that target. Return null otherwise. */
+
+static tree
+get_upcast_warning_type (vec<tree, va_gc> &targets, tree lhs_type)
+{
+ tree lhs_target = TYPE_MAIN_VARIANT (TREE_TYPE (lhs_type));
+ tree alt_target = get_common_target (targets, lhs_target, targets[0]);
+ if (alt_target == lhs_target
+ || decl_anon_ns_mem_p (alt_target))
+ return NULL_TREE;
+
+ alt_target = cp_build_qualified_type
+ (alt_target, cp_type_quals (TREE_TYPE (lhs_type)));
+ if (TREE_CODE (lhs_type) == POINTER_TYPE)
+ return build_pointer_type (alt_target);
+ else
+ return build_reference_type (alt_target);
+}
+
/* Returns the kind of template specialization we are currently
processing, given that it's declaration contained N_CLASS_SCOPES
explicit scope qualifications. */
@@ -633,8 +703,13 @@ poplevel (int keep, int reverse, int fun
leaving_for_scope
= current_binding_level->kind == sk_for && flag_new_for_scope == 1;
- /* Before we remove the declarations first check for unused variables. */
- if ((warn_unused_variable || warn_unused_but_set_variable)
+ /* Before we remove the declarations first check for unused variables
+ and variables with lax types. */
+ if ((warn_unused_variable
+ || warn_unused_but_set_variable
+ || (warn_upcast_var
+ && cfun
+ && current_function_decl_target_types))
&& current_binding_level->kind != sk_template_parms
&& !processing_template_decl)
for (tree d = getdecls (); d; d = TREE_CHAIN (d))
@@ -644,7 +719,8 @@ poplevel (int keep, int reverse, int fun
getdecls is built. */
decl = TREE_CODE (d) == TREE_LIST ? TREE_VALUE (d) : d;
tree type = TREE_TYPE (decl);
- if (VAR_P (decl)
+ if ((warn_unused_variable || warn_unused_but_set_variable)
+ && VAR_P (decl)
&& (! TREE_USED (decl) || !DECL_READ_P (decl))
&& ! DECL_IN_SYSTEM_HEADER (decl)
&& DECL_NAME (decl) && ! DECL_ARTIFICIAL (decl)
@@ -666,6 +742,25 @@ poplevel (int keep, int reverse, int fun
unused_but_set_errorcount = errorcount;
}
}
+ if (VAR_P (decl)
+ && !TREE_ADDRESSABLE (decl)
+ && warn_upcast_var
+ && cfun
+ && current_function_decl_target_types)
+ {
+ cp_decl_target_types dummy = { decl, NULL };
+ cp_decl_target_types *entry
+ = current_function_decl_target_types->find (&dummy);
+ if (entry && !vec_safe_is_empty (entry->target_types))
+ {
+ tree var_type = TREE_TYPE (decl);
+ tree alt_type = get_upcast_warning_type (*entry->target_types,
+ var_type);
+ if (alt_type)
+ warning (OPT_Wupcast_var, "%q+D could have type %qT"
+ " instead of %qT", decl, alt_type, var_type);
+ }
+ }
}
/* Remove declarations for all the DECLs in this level. */
@@ -14294,6 +14389,19 @@ finish_function (int flags)
TREE_NO_WARNING (fndecl) = 1;
}
+ if (!DECL_VIRTUAL_P (fndecl)
+ && !vec_safe_is_empty (current_function_result_target_types)
+ && warn_upcast_result)
+ {
+ tree ret_type = TREE_TYPE (fntype);
+ tree alt_type = get_upcast_warning_type
+ (*current_function_result_target_types, ret_type);
+ if (alt_type)
+ warning_at (DECL_SOURCE_LOCATION (fndecl), OPT_Wupcast_result,
+ "function could return %qT instead of %qT",
+ alt_type, ret_type);
+ }
+
/* Store the end of the function, so that we get good line number
info for the epilogue. */
cfun->function_end_locus = input_location;
@@ -14345,6 +14453,8 @@ finish_function (int flags)
f->bindings = NULL;
f->extern_decl_map = NULL;
f->infinite_loops = NULL;
+ f->x_result_target_types = NULL;
+ f->x_decl_target_types = NULL;
}
/* Clear out the bits we don't need. */
local_names = NULL;
===================================================================
@@ -74,6 +74,77 @@ static void warn_args_num (location_t, t
static int convert_arguments (tree, vec<tree, va_gc> **, tree, int,
tsubst_flags_t);
+hashval_t
+cp_decl_target_types_hasher::hash (cp_decl_target_types *entry)
+{
+ return DECL_UID (entry->var);
+}
+
+bool
+cp_decl_target_types_hasher::equal (cp_decl_target_types *a,
+ cp_decl_target_types *b)
+{
+ return a->var == b->var;
+}
+
+/* RHS is being assigned to a variable of type LHS_TYPE or is being returned
+ from a function that returns LHS_TYPE. If LHS_TYPE is a pointer to a class,
+ update TARGET_TYPES so that it would be safe to replace TREE_TYPE (LHS_TYPE)
+ with some type X if each member of TARGET_TYPES is, or is derived
+ from, X. */
+
+static void
+joust_upcast_types (vec<tree, va_gc> *&target_types, tree lhs_type, tree rhs)
+{
+ /* Only consider pointers to classes and references to classes. */
+ if (!POINTER_TYPE_P (lhs_type))
+ return;
+ tree lhs_target = TYPE_MAIN_VARIANT (TREE_TYPE (lhs_type));
+ if (!CLASS_TYPE_P (lhs_target))
+ return;
+
+ /* Ignore null values, since they could come from type-specific
+ macros (like GCC's NULL_RTX). */
+ if (null_ptr_cst_p (rhs) || integer_zerop (rhs))
+ return;
+
+ tree rhs_type = TREE_TYPE (rhs);
+ if (TREE_CODE (lhs_type) == TREE_CODE (rhs_type)
+ || TREE_CODE (lhs_type) == REFERENCE_TYPE)
+ {
+ /* We're only interested in cases where the target of RHS_TYPE
+ is derived from the target of LHS_TYPE. */
+ tree rhs_target = (TREE_CODE (lhs_type) == TREE_CODE (rhs_type)
+ ? TREE_TYPE (rhs_type) : rhs_type);
+ rhs_target = TYPE_MAIN_VARIANT (rhs_target);
+ if (CLASS_TYPE_P (rhs_target)
+ && lhs_target != rhs_target
+ && DERIVED_FROM_P (lhs_target, rhs_target))
+ {
+ /* Do nothing if RHS_TARGET is derived from an existing type. */
+ unsigned int i;
+ tree other_target;
+ FOR_EACH_VEC_SAFE_ELT (target_types, i, other_target)
+ if (other_target == rhs_target
+ || DERIVED_FROM_P (other_target, rhs_target))
+ return;
+
+ /* Remove any entries derived from RHS_TARGET. */
+ unsigned int j = 0;
+ FOR_EACH_VEC_SAFE_ELT (target_types, i, other_target)
+ if (!DERIVED_FROM_P (rhs_target, other_target))
+ (*target_types)[j++] = other_target;
+
+ vec_safe_truncate (target_types, j);
+ vec_safe_push (target_types, rhs_target);
+ return;
+ }
+ }
+ /* The worst case: keep the original target. */
+ vec_safe_truncate (target_types, 0);
+ vec_safe_push (target_types, lhs_target);
+}
+
/* Do `exp = require_complete_type (exp);' to make sure exp
does not have an incomplete type. (That includes void types.)
Returns error_mark_node if the VALUE does not have
@@ -7307,6 +7378,33 @@ build_modify_expr (location_t /*location
return cp_build_modify_expr (lhs, modifycode, rhs, tf_warning_or_error);
}
+/* Record an assignment from RHS to LHS for -Wupcast-var. */
+
+void
+upcast_var_record_type (tree lhs, tree rhs)
+{
+ tree lhstype = TREE_TYPE (lhs);
+ if (VAR_P (lhs)
+ && !TREE_STATIC (lhs)
+ && DECL_CONTEXT (lhs) == current_function_decl
+ && POINTER_TYPE_P (lhstype)
+ && CLASS_TYPE_P (TREE_TYPE (lhstype)))
+ {
+ cp_decl_target_types dummy = { lhs, NULL };
+ if (!current_function_decl_target_types)
+ current_function_decl_target_types
+ = hash_table<cp_decl_target_types_hasher>::create_ggc (32);
+ cp_decl_target_types **slot
+ = current_function_decl_target_types->find_slot (&dummy, INSERT);
+ if (!*slot)
+ {
+ *slot = ggc_alloc <cp_decl_target_types> ();
+ **slot = dummy;
+ }
+ joust_upcast_types ((*slot)->target_types, lhstype, rhs);
+ }
+}
+
/* Build an assignment expression of lvalue LHS from value RHS.
MODIFYCODE is the code for a binary operator that we use
to combine the old value of LHS with RHS to get the new value.
@@ -7427,6 +7525,9 @@ cp_build_modify_expr (tree lhs, enum tre
break;
}
+ if (warn_upcast_var)
+ upcast_var_record_type (lhs, rhs);
+
if (modifycode == INIT_EXPR)
{
if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
@@ -8537,6 +8638,10 @@ check_return_expr (tree retval, bool *no
functype = type;
}
+ if (warn_upcast_result)
+ joust_upcast_types (current_function_result_target_types,
+ functype, retval);
+
/* When no explicit return-value is given in a function with a named
return value, the named return value is used. */
result = DECL_RESULT (current_function_decl);
===================================================================
@@ -814,6 +814,9 @@ store_init_value (tree decl, tree init,
value = extend_ref_init_temps (decl, value, cleanups);
+ if (warn_upcast_var)
+ upcast_var_record_type (decl, init);
+
/* In C++11 constant expression is a semantic, not syntactic, property.
In C++98, make sure that what we thought was a constant expression at
template definition time is still constant and otherwise perform this
===================================================================
@@ -0,0 +1,136 @@
+// { dg-options "-Wupcast-result -std=c++11" }
+
+struct B {};
+struct B1 : public B {};
+struct B11 : public B1 {};
+struct B12 : public B1 {};
+struct B121 : public B12 {};
+struct B2 : public B {};
+namespace { struct B3 : public B {}; }
+
+B *
+f1 (int x) // { dg-warning "function could return 'B1\\*'" }
+{
+ if (x == 1)
+ return new B11;
+ if (x == 2)
+ return new B12;
+ if (x == 3)
+ return (B *) 0;
+ if (x == 4)
+ return nullptr;
+ if (x == 5)
+ return 0;
+ if (x == 6)
+ return (B1 *) 0;
+ if (x == 7)
+ return (B11 *) 0;
+ if (x == 8)
+ return (B12 *) 0;
+ return new B1;
+}
+
+B *
+f2 (int x) // { dg-warning "function could return 'B1\\*'" }
+{
+ if (x == 1)
+ return new B12;
+ if (x == 2)
+ return new B11;
+ return new B1;
+}
+
+B *
+f3 (int x) // { dg-warning "function could return 'B1\\*'" }
+{
+ if (x == 1)
+ return new B1;
+ if (x == 2)
+ return new B11;
+ return new B12;
+}
+
+B *
+f4 (int x) // { dg-warning "function could return 'B1\\*'" }
+{
+ if (x == 1)
+ return new B11;
+ return new B12;
+}
+
+B *
+f5 (int x)
+{
+ if (x == 1)
+ return new B1;
+ return new B2;
+}
+
+B *
+f6 (int x)
+{
+ if (x == 1)
+ return new B11;
+ return new B2;
+}
+
+B *
+f7 (int x)
+{
+ if (x == 1)
+ return new B12;
+ return new B2;
+}
+
+B *
+f8 (int x) // { dg-warning "function could return 'B12\\*'" }
+{
+ if (x == 1)
+ return new B12;
+ return new B121;
+}
+
+B *
+f9 ()
+{
+ return (B *) new B1;
+}
+
+B *
+f10 ()
+{
+ return new B3;
+}
+
+B *
+f11 ()
+{
+ return new B;
+}
+
+B *
+f12 ()
+{
+ return (B2 *) 0;
+}
+
+int *
+f13 ()
+{
+ return new int;
+}
+
+int
+f14 ()
+{
+ return 0;
+}
+
+B **
+f15 (B **x)
+{
+ return x;
+}
+
+struct X1 { B *foo () { return new B1; } }; // { dg-warning "function could return 'B1\\*'" }
+struct X2 { virtual B *foo () { return new B1; } };
===================================================================
@@ -0,0 +1,83 @@
+// { dg-options "-Wupcast-result" }
+
+struct B {};
+struct B1 : virtual public B {};
+struct B2 : virtual public B {};
+struct B3 : virtual public B {};
+struct B4 : virtual public B {};
+struct B1B2 : virtual public B1, virtual public B2 {};
+struct B1B2B3 : virtual public B1, virtual public B2, virtual public B3 {};
+struct B1B2B4 : virtual public B1, virtual public B2, virtual public B4 {};
+struct B2B3 : virtual public B2, virtual public B3 {};
+struct B3B4 : virtual public B3, virtual public B4 {};
+
+B *
+f1 (int x) // { dg-warning "function could return 'B1\\*'" }
+{
+ if (x == 1)
+ return new B1();
+ return new B1B2();
+}
+
+B *
+f2 (int x) // { dg-warning "function could return 'B2\\*'" }
+{
+ if (x == 1)
+ return new B2();
+ return new B1B2();
+}
+
+B *
+f3 (int x) // { dg-warning "function could return 'B2\\*'" }
+{
+ if (x == 1)
+ return new B1B2();
+ if (x == 2)
+ return new B1B2B3();
+ return new B2();
+}
+
+B *
+f4 (int x) // { dg-warning "function could return 'B2\\*'" }
+{
+ if (x == 1)
+ return new B1B2B3();
+ if (x == 2)
+ return new B1B2B4();
+ return new B2();
+}
+
+const volatile B *
+f5 (int x) // { dg-warning "function could return 'const volatile B1\\*'" }
+{
+ if (x == 1)
+ return new B1B2B3();
+ if (x == 2)
+ return new B1B2B4();
+ return new B1();
+}
+
+// Expect B1 to picked over B2, since it's listed first.
+const B *
+f6 (int x) // { dg-warning "function could return 'const B1\\*'" }
+{
+ if (x == 1)
+ return new B1B2;
+ return new B1B2B3;
+}
+
+volatile B *
+f7 (int x) // { dg-warning "function could return 'volatile B2\\*'" }
+{
+ if (x == 1)
+ return new B1B2;
+ return new B2B3;
+}
+
+B *
+f8 (int x)
+{
+ if (x == 1)
+ return new B1B2;
+ return new B3B4;
+}
===================================================================
@@ -0,0 +1,31 @@
+// { dg-options "-Wupcast-result" }
+
+struct B {};
+struct B1 : public B {};
+struct B2 : public B {};
+struct B21 : public B2 {};
+
+B2 b2;
+const B2 cb2;
+B21 b21;
+const B21 cb21;
+
+const B &
+f1 (int x) // { dg-warning "function could return 'const B2&'" }
+{
+ if (x == 1)
+ return cb2;
+ return cb21;
+}
+
+B &
+f2 (B1 &y) // { dg-warning "function could return 'B1&'" }
+{
+ return y;
+}
+
+B &
+f3 (B1 &y)
+{
+ return (B &) y;
+}
===================================================================
@@ -0,0 +1,15 @@
+// { dg-options "-Wupcast-result" }
+
+struct B {};
+struct B1 : public B {};
+struct __attribute__((no_upcast_warning)) B11 : public B1 {};
+struct B111 : public B11 {};
+struct B112 : public B11 {};
+
+B *
+f1 (int x) // { dg-warning "function could return 'B1\\*'" }
+{
+ if (x == 1)
+ return new B111;
+ return new B112;
+}
===================================================================
@@ -0,0 +1,160 @@
+// { dg-options "-Wupcast-var -std=c++11" }
+
+struct B {};
+struct B1 : public B {};
+struct B11 : public B1 {};
+struct B12 : public B1 {};
+struct B121 : public B12 {};
+struct B2 : public B {};
+namespace { struct B3 : public B {}; }
+
+void
+f1 ()
+{
+ B *x; // { dg-warning "could have type 'B1\\*'" }
+ x = new B11();
+ x = new B12();
+ x = (B *) 0;
+ x = nullptr;
+ x = 0;
+ x = (B1 *) 0;
+ x = (B11 *) 0;
+ x = (B12 *) 0;
+ x = new B1;
+}
+
+void
+f2 (void)
+{
+ B *x = new B12(); // { dg-warning "could have type 'B1\\*'" }
+ x = new B11();
+ x = new B1();
+}
+
+void
+f3 (void)
+{
+ B *x = new B1(); // { dg-warning "could have type 'B1\\*'" }
+ x = new B12();
+ x = new B11();
+}
+
+void
+f4 ()
+{
+ B *x = new B11(); // { dg-warning "could have type 'B1\\*'" }
+ x = new B12();
+}
+
+void
+f5 ()
+{
+ B *x = new B1;
+ x = new B2;
+}
+
+void
+f6 ()
+{
+ B *x = new B11;
+ x = new B2;
+}
+
+void
+f7 ()
+{
+ B *x = new B12;
+ x = new B2;
+}
+
+void
+f8 ()
+{
+ B *x = new B12(); // { dg-warning "could have type 'B12\\*'" }
+ x = new B121();
+}
+
+void
+f9 ()
+{
+ B *x = (B *) new B1;
+}
+
+void
+f10 ()
+{
+ B *x = new B3;
+}
+
+void
+f11 ()
+{
+ B *x = new B;
+}
+
+void
+f12 ()
+{
+ B *x = (B2 *) 0;
+}
+
+void
+f13 ()
+{
+ int *x = new int;
+}
+
+void
+f14 ()
+{
+ int x = 0;
+}
+
+void
+f15 (B **y)
+{
+ B **x = y;
+}
+
+void
+f15 (B *y)
+{
+ y = new B1;
+}
+
+void foo (B **x);
+
+void
+f16 ()
+{
+ B *x = new B11;
+ foo (&x);
+}
+
+void
+f17 ()
+{
+ static B *x = new B11;
+}
+
+void
+f18 ()
+{
+ static B *x = new B11;
+ foo (&x);
+}
+
+void
+f19 ()
+{
+ extern B *x;
+ x = new B11;
+}
+
+void
+f20 ()
+{
+ extern B *x;
+ x = new B11;
+ foo (&x);
+}