diff options
author | Martin Liska <mliska@suse.cz> | 2022-10-28 10:02:34 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2022-10-28 10:02:34 +0200 |
commit | 1eb021edb27e26f95cda63df121f6bc951647599 (patch) | |
tree | 7f132fded85bd7d05d81cd4c1227da2fd0c3c2d5 /gcc/cp | |
parent | 62e475bad0d668c432bb97113cbf73fa281b8b55 (diff) | |
parent | 0607307768b66a90e27c5bc91a247acc938f070e (diff) | |
download | gcc-1eb021edb27e26f95cda63df121f6bc951647599.zip gcc-1eb021edb27e26f95cda63df121f6bc951647599.tar.gz gcc-1eb021edb27e26f95cda63df121f6bc951647599.tar.bz2 |
Merge branch 'master' into devel/sphinx
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/ChangeLog | 31 | ||||
-rw-r--r-- | gcc/cp/call.cc | 148 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 28 | ||||
-rw-r--r-- | gcc/cp/mangle.cc | 68 | ||||
-rw-r--r-- | gcc/cp/name-lookup.cc | 7 | ||||
-rw-r--r-- | gcc/cp/typeck.cc | 14 |
6 files changed, 258 insertions, 38 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 550515c..d065fd1 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,34 @@ +2022-10-27 Jakub Jelinek <jakub@redhat.com> + + PR c++/107379 + * name-lookup.cc (push_namespace): Call find_namespace_slot again + after pushdecl as the hash table might be expanded during pushdecl. + +2022-10-27 Nathan Sidwell <nathan@acm.org> + + * mangle.cc (write_closure_template_head): New. + (write_closure_type_name): Call it. + +2022-10-27 Jakub Jelinek <jakub@redhat.com> + + PR c++/107382 + PR c++/107383 + * typeck.cc (cp_build_binary_op): Don't compute semantic_result_type + if result_type is NULL. + +2022-10-26 Marek Polacek <polacek@redhat.com> + + PR c++/106393 + * call.cc (expr_represents_temporary_p): New, factored out of... + (conv_binds_ref_to_temporary): ...here. Don't return false just + because a ck_base is missing. Use expr_represents_temporary_p. + (do_warn_dangling_reference): New. + (maybe_warn_dangling_reference): New. + (extend_ref_init_temps): Call maybe_warn_dangling_reference. + * cp-tree.h: Adjust comment. + * typeck.cc (check_return_expr): Suppress -Wdangling-reference + warnings. + 2022-10-25 Nathan Sidwell <nathan@acm.org> * parser.cc (synthesize_implicit_template_parm): Fix thinko about diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 6a34e9c..951b9fd 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -9313,6 +9313,16 @@ conv_binds_ref_to_prvalue (conversion *c) return conv_is_prvalue (next_conversion (c)); } +/* True iff EXPR represents a (subobject of a) temporary. */ + +static bool +expr_represents_temporary_p (tree expr) +{ + while (handled_component_p (expr)) + expr = TREE_OPERAND (expr, 0); + return TREE_CODE (expr) == TARGET_EXPR; +} + /* True iff C is a conversion that binds a reference to a temporary. This is a superset of conv_binds_ref_to_prvalue: here we're also interested in xvalues. */ @@ -9330,18 +9340,14 @@ conv_binds_ref_to_temporary (conversion *c) struct Derived : Base {}; const Base& b(Derived{}); where we bind 'b' to the Base subobject of a temporary object of type - Derived. The subobject is an xvalue; the whole object is a prvalue. */ - if (c->kind != ck_base) - return false; - c = next_conversion (c); - if (c->kind == ck_identity && c->u.expr) - { - tree expr = c->u.expr; - while (handled_component_p (expr)) - expr = TREE_OPERAND (expr, 0); - if (TREE_CODE (expr) == TARGET_EXPR) - return true; - } + Derived. The subobject is an xvalue; the whole object is a prvalue. + + The ck_base doesn't have to be present for cases like X{}.m. */ + if (c->kind == ck_base) + c = next_conversion (c); + if (c->kind == ck_identity && c->u.expr + && expr_represents_temporary_p (c->u.expr)) + return true; return false; } @@ -13428,6 +13434,121 @@ initialize_reference (tree type, tree expr, return expr; } +/* Helper for maybe_warn_dangling_reference to find a problematic CALL_EXPR + that initializes the LHS (and at least one of its arguments represents + a temporary, as outlined in maybe_warn_dangling_reference), or NULL_TREE + if none found. For instance: + + const S& s = S().self(); // S::self (&TARGET_EXPR <...>) + const int& r = (42, f(1)); // f(1) + const int& t = b ? f(1) : f(2); // f(1) + const int& u = b ? f(1) : f(g); // f(1) + const int& v = b ? f(g) : f(2); // f(2) + const int& w = b ? f(g) : f(g); // NULL_TREE + const int& y = (f(1), 42); // NULL_TREE + const int& z = f(f(1)); // f(f(1)) + + EXPR is the initializer. */ + +static tree +do_warn_dangling_reference (tree expr) +{ + STRIP_NOPS (expr); + switch (TREE_CODE (expr)) + { + case CALL_EXPR: + { + tree fndecl = cp_get_callee_fndecl_nofold (expr); + if (!fndecl + || warning_suppressed_p (fndecl, OPT_Wdangling_reference) + || !warning_enabled_at (DECL_SOURCE_LOCATION (fndecl), + OPT_Wdangling_reference) + /* If the function doesn't return a reference, don't warn. This + can be e.g. + const int& z = std::min({1, 2, 3, 4, 5, 6, 7}); + which doesn't dangle: std::min here returns an int. */ + || !TYPE_REF_OBJ_P (TREE_TYPE (TREE_TYPE (fndecl)))) + return NULL_TREE; + + /* Here we're looking to see if any of the arguments is a temporary + initializing a reference parameter. */ + for (int i = 0; i < call_expr_nargs (expr); ++i) + { + tree arg = CALL_EXPR_ARG (expr, i); + /* Check that this argument initializes a reference, except for + the argument initializing the object of a member function. */ + if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl) + && !TYPE_REF_P (TREE_TYPE (arg))) + continue; + /* It could also be another call taking a temporary and returning + it and initializing this reference parameter. */ + if (do_warn_dangling_reference (arg)) + return expr; + STRIP_NOPS (arg); + if (TREE_CODE (arg) == ADDR_EXPR) + arg = TREE_OPERAND (arg, 0); + if (expr_represents_temporary_p (arg)) + return expr; + /* Don't warn about member function like: + std::any a(...); + S& s = a.emplace<S>({0}, 0); + which constructs a new object and returns a reference to it, but + we still want to detect: + struct S { const S& self () { return *this; } }; + const S& s = S().self(); + where 's' dangles. If we've gotten here, the object this function + is invoked on is not a temporary. */ + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl)) + break; + } + return NULL_TREE; + } + case COMPOUND_EXPR: + return do_warn_dangling_reference (TREE_OPERAND (expr, 1)); + case COND_EXPR: + if (tree t = do_warn_dangling_reference (TREE_OPERAND (expr, 1))) + return t; + return do_warn_dangling_reference (TREE_OPERAND (expr, 2)); + case PAREN_EXPR: + return do_warn_dangling_reference (TREE_OPERAND (expr, 0)); + default: + return NULL_TREE; + } +} + +/* Implement -Wdangling-reference, to detect cases like + + int n = 1; + const int& r = std::max(n - 1, n + 1); // r is dangling + + This creates temporaries from the arguments, returns a reference to + one of the temporaries, but both temporaries are destroyed at the end + of the full expression. + + This works by checking if a reference is initialized with a function + that returns a reference, and at least one parameter of the function + is a reference that is bound to a temporary. It assumes that such a + function actually returns one of its arguments. + + DECL is the reference being initialized, INIT is the initializer. */ + +static void +maybe_warn_dangling_reference (const_tree decl, tree init) +{ + if (!warn_dangling_reference) + return; + if (!TYPE_REF_P (TREE_TYPE (decl))) + return; + if (tree call = do_warn_dangling_reference (init)) + { + auto_diagnostic_group d; + if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wdangling_reference, + "possibly dangling reference to a temporary")) + inform (EXPR_LOCATION (call), "the temporary was destroyed at " + "the end of the full expression %qE", call); + } +} + /* If *P is an xvalue expression, prevent temporary lifetime extension if it gets used to initialize a reference. */ @@ -13525,6 +13646,9 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups, tree type = TREE_TYPE (init); if (processing_template_decl) return init; + + maybe_warn_dangling_reference (decl, init); + if (TYPE_REF_P (type)) init = extend_ref_init_temps_1 (decl, init, cleanups, cond_guard); else diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 867096b..6d84514 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -459,7 +459,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; TI_PENDING_TEMPLATE_FLAG. TEMPLATE_PARMS_FOR_INLINE. DELETE_EXPR_USE_VEC (in DELETE_EXPR). - (TREE_CALLS_NEW) (in _EXPR or _REF) (commented-out). ICS_ELLIPSIS_FLAG (in _CONV) DECL_INITIALIZED_P (in VAR_DECL) TYPENAME_IS_CLASS_P (in TYPENAME_TYPE) @@ -4508,30 +4507,6 @@ get_vec_init_expr (tree t) #define OPAQUE_ENUM_P(TYPE) \ (TREE_CODE (TYPE) == ENUMERAL_TYPE && ENUM_IS_OPAQUE (TYPE)) -/* Determines whether an ENUMERAL_TYPE has an explicit - underlying type. */ -#define ENUM_FIXED_UNDERLYING_TYPE_P(NODE) (TYPE_LANG_FLAG_5 (NODE)) - -/* Returns the underlying type of the given enumeration type. The - underlying type is determined in different ways, depending on the - properties of the enum: - - - In C++0x, the underlying type can be explicitly specified, e.g., - - enum E1 : char { ... } // underlying type is char - - - In a C++0x scoped enumeration, the underlying type is int - unless otherwises specified: - - enum class E2 { ... } // underlying type is int - - - Otherwise, the underlying type is determined based on the - values of the enumerators. In this case, the - ENUM_UNDERLYING_TYPE will not be set until after the definition - of the enumeration is completed by finish_enum. */ -#define ENUM_UNDERLYING_TYPE(TYPE) \ - TREE_TYPE (ENUMERAL_TYPE_CHECK (TYPE)) - /* [dcl.init.aggr] An aggregate is an array or a class with no user-provided @@ -4567,6 +4542,9 @@ get_vec_init_expr (tree t) When appearing in a CONSTRUCTOR, the expression is an unconverted compound literal. + When appearing in a CALL_EXPR, it means that it is a call to + a constructor. + When appearing in a FIELD_DECL, it means that this field has been duly initialized in its constructor. */ #define TREE_HAS_CONSTRUCTOR(NODE) (TREE_LANG_FLAG_4 (NODE)) diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 1215463..e396218 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -1727,6 +1727,66 @@ write_unnamed_type_name (const tree type) write_compact_number (discriminator); } +// A template head, for templated lambdas. +// <template-head> ::= Tp* Ty +// Tp* Tn <type> +// Tp* Tt <template-head> E +// New in ABI=18. Returns true iff we emitted anything -- used for ABI +// version warning. + +static bool +write_closure_template_head (tree tmpl) +{ + bool any = false; + + // We only need one level of template parms + tree inner = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); + + for (int ix = 0, len = TREE_VEC_LENGTH (inner); ix != len; ix++) + { + tree parm = TREE_VEC_ELT (inner, ix); + if (parm == error_mark_node) + continue; + parm = TREE_VALUE (parm); + + if (DECL_VIRTUAL_P (parm)) + // A synthetic parm, we're done. + break; + + any = true; + if (abi_version_at_least (18)) + { + if (TREE_CODE (parm) == PARM_DECL + ? TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm)) + : TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (parm))) + write_string ("Tp"); + + switch (TREE_CODE (parm)) + { + default: + gcc_unreachable (); + + case TYPE_DECL: + write_string ("Ty"); + break; + + case PARM_DECL: + write_string ("Tn"); + write_type (TREE_TYPE (parm)); + break; + + case TEMPLATE_DECL: + write_string ("Tt"); + write_closure_template_head (parm); + write_string ("E"); + break; + } + } + } + + return any; +} + /* <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters */ @@ -1740,6 +1800,14 @@ write_closure_type_name (const tree type) MANGLE_TRACE_TREE ("closure-type-name", type); write_string ("Ul"); + + if (auto ti = maybe_template_info (fn)) + if (write_closure_template_head (TI_TEMPLATE (ti))) + // If there were any explicit template parms, we may need to + // issue a mangling diagnostic. + if (abi_warn_or_compat_version_crosses (18)) + G.need_abi_warning = true; + write_method_parms (parms, /*method_p=*/1, fn); write_char ('E'); write_compact_number (LAMBDA_EXPR_DISCRIMINATOR (lambda)); diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index 14e937d..dfa6fb4 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -8596,6 +8596,13 @@ push_namespace (tree name, bool make_inline) /* This should find the slot created by pushdecl. */ gcc_checking_assert (slot && *slot == ns); } + else + { + /* pushdecl could have expanded the hash table, so + slot might be invalid. */ + slot = find_namespace_slot (current_namespace, name); + gcc_checking_assert (slot); + } make_namespace_finish (ns, slot); /* Add the anon using-directive here, we don't do it in diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index ab6979b..2e0fd8f 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -6179,7 +6179,8 @@ cp_build_binary_op (const op_location_t &location, } if (may_need_excess_precision && (orig_type0 != type0 || orig_type1 != type1) - && build_type == NULL_TREE) + && build_type == NULL_TREE + && result_type) { gcc_assert (common); semantic_result_type = cp_common_type (orig_type0, orig_type1); @@ -11246,6 +11247,17 @@ check_return_expr (tree retval, bool *no_warning) if (processing_template_decl) return saved_retval; + /* A naive attempt to reduce the number of -Wdangling-reference false + positives: if we know that this function can return a variable with + static storage duration rather than one of its parameters, suppress + the warning. */ + if (warn_dangling_reference + && TYPE_REF_P (functype) + && bare_retval + && VAR_P (bare_retval) + && TREE_STATIC (bare_retval)) + suppress_warning (current_function_decl, OPT_Wdangling_reference); + /* Actually copy the value returned into the appropriate location. */ if (retval && retval != result) retval = cp_build_init_expr (result, retval); |