From 0470bdc5b2b4ddff2d2ee9db11a8c5895abda50f Mon Sep 17 00:00:00 2001
From: Barrett Adair <barrettellisadair@gmail.com>
Date: Fri, 20 Aug 2021 15:37:36 -0500
Subject: [PATCH] Fix trailing return type bugs
---
gcc/cp/cp-tree.h | 2 +-
gcc/cp/parser.c | 13 ++++-
gcc/cp/parser.h | 3 +
gcc/cp/pt.c | 58 +++++++++++++++++--
gcc/cp/semantics.c | 9 ++-
gcc/testsuite/g++.dg/template/canon-type-15.C | 7 +++
gcc/testsuite/g++.dg/template/canon-type-16.C | 6 ++
gcc/testsuite/g++.dg/template/canon-type-17.C | 5 ++
gcc/testsuite/g++.dg/template/canon-type-18.C | 6 ++
.../g++.dg/template/dependent-expr11.C | 6 ++
.../g++.dg/template/dependent-name15.C | 18 ++++++
.../g++.dg/template/dependent-name16.C | 14 +++++
12 files changed, 136 insertions(+), 11 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/template/canon-type-15.C
create mode 100644 gcc/testsuite/g++.dg/template/canon-type-16.C
create mode 100644 gcc/testsuite/g++.dg/template/canon-type-17.C
create mode 100644 gcc/testsuite/g++.dg/template/canon-type-18.C
create mode 100644 gcc/testsuite/g++.dg/template/dependent-expr11.C
create mode 100644 gcc/testsuite/g++.dg/template/dependent-name15.C
create mode 100644 gcc/testsuite/g++.dg/template/dependent-name16.C
@@ -7537,7 +7537,7 @@ extern tree process_outer_var_ref (tree, tsubst_flags_t, bool force_use = false
extern cp_expr finish_id_expression (tree, tree, tree,
cp_id_kind *,
bool, bool, bool *,
- bool, bool, bool, bool,
+ bool, bool, bool, bool, bool,
const char **,
location_t);
extern tree finish_typeof (tree);
@@ -6011,7 +6011,8 @@ cp_parser_primary_expression (cp_parser *parser,
parser->integral_constant_expression_p,
parser->allow_non_integral_constant_expression_p,
&parser->non_integral_constant_expression_p,
- template_p, done, address_p,
+ template_p, parser->in_trailing_return_type_p,
+ done, address_p,
template_arg_p,
&error_msg,
id_expression.get_location ()));
@@ -11256,6 +11257,7 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
/*allow_non_integral_constant_expression_p=*/false,
/*non_integral_constant_expression_p=*/NULL,
/*template_p=*/false,
+ /*trailing_return_type_p=*/false,
/*done=*/true,
/*address_p=*/false,
/*template_arg_p=*/false,
@@ -16192,6 +16194,7 @@ cp_parser_decltype_expr (cp_parser *parser,
/*allow_non_integral_constant_expression_p=*/true,
&non_integral_constant_expression_p,
/*template_p=*/false,
+ parser->in_trailing_return_type_p,
/*done=*/true,
/*address_p=*/false,
/*template_arg_p=*/false,
@@ -24078,8 +24081,11 @@ cp_parser_template_type_arg (cp_parser *parser)
static tree
cp_parser_trailing_type_id (cp_parser *parser)
{
- return cp_parser_type_id_1 (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
+ parser->in_trailing_return_type_p = true;
+ tree result = cp_parser_type_id_1 (parser, CP_PARSER_FLAGS_TYPENAME_OPTIONAL,
false, true, NULL);
+ parser->in_trailing_return_type_p = false;
+ return result;
}
/* Parse a type-specifier-seq.
@@ -44635,7 +44641,8 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
= finish_id_expression (varid, variant, parser->scope,
&idk, false, true,
&parser->non_integral_constant_expression_p,
- template_p, true, false, false, &error_msg,
+ template_p, parser->in_trailing_return_type_p,
+ true, false, false, &error_msg,
varid.get_location ());
if (error_msg)
cp_parser_error (parser, error_msg);
@@ -419,6 +419,9 @@ struct GTY(()) cp_parser {
member definition using a generic type, it is the sk_class scope. */
cp_binding_level* implicit_template_scope;
+ /* True if parsing a trailing return type.*/
+ bool in_trailing_return_type_p;
+
/* True if parsing a result type in a compound requirement. This permits
constrained-type-specifiers inside what would normally be a trailing
return type. */
@@ -16593,10 +16593,9 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if (DECL_NAME (t) == this_identifier && current_class_ptr)
return current_class_ptr;
- /* This can happen for a parameter name used later in a function
- declaration (such as in a late-specified return type). Just
- make a dummy decl, since it's only used for its type. */
- gcc_assert (cp_unevaluated_operand != 0);
+ gcc_assert (cp_unevaluated_operand != 0
+ || TYPE_HAS_LATE_RETURN_TYPE (TREE_TYPE (DECL_CONTEXT (t))));
+
r = tsubst_decl (t, args, complain);
/* Give it the template pattern as its context; its true context
hasn't been instantiated yet and this is good enough for
@@ -19643,6 +19642,7 @@ tsubst_copy_and_build (tree t,
/*allow_non_integral_constant_expression_p=*/(cxx_dialect >= cxx11),
&non_integral_constant_expression_p,
/*template_p=*/false,
+ /*in_trailing_return_type_p=*/false,
/*done=*/true,
/*address_p=*/false,
/*template_arg_p=*/false,
@@ -20859,6 +20859,25 @@ tsubst_copy_and_build (tree t,
case PARM_DECL:
{
tree r = tsubst_copy (t, args, complain, in_decl);
+ tree context = r != error_mark_node
+ && tree_contains_struct[TREE_CODE (r)][TS_DECL_MINIMAL]
+ ? DECL_CONTEXT (r) : NULL_TREE;
+
+ /* if we're instantiating a template in a trailing return type,
+ we need the function's parameters in scope */
+ bool needs_context_push = context && TREE_TYPE (context)
+ && DECL_DECLARES_FUNCTION_P (context)
+ && TYPE_HAS_LATE_RETURN_TYPE (TREE_TYPE (context));
+
+ if (needs_context_push)
+ {
+ push_access_scope (context);
+ push_deferring_access_checks (dk_no_deferred);
+ if (LAMBDA_FUNCTION_P (context))
+ start_lambda_scope (r);
+ ++function_depth;
+ }
+
/* ??? We're doing a subset of finish_id_expression here. */
if (tree wrap = maybe_get_tls_wrapper_call (r))
/* Replace an evaluated use of the thread_local variable with
@@ -20871,6 +20890,16 @@ tsubst_copy_and_build (tree t,
/* If the original type was a reference, we'll be wrapped in
the appropriate INDIRECT_REF. */
r = convert_from_reference (r);
+
+ if (needs_context_push)
+ {
+ --function_depth;
+ if (LAMBDA_FUNCTION_P (context))
+ finish_lambda_scope ();
+ pop_deferring_access_checks ();
+ pop_access_scope (context);
+ }
+
RETURN (r);
}
@@ -27766,6 +27795,21 @@ dependent_template_arg_p (tree arg)
return value_dependent_expression_p (arg);
}
+/* Identify any expressions dependent on a function
+ template's PARM_DECL */
+static tree
+find_dependent_parm_decl_r (tree *tp, int *walk_subtrees, void*)
+{
+ tree t = *tp;
+ if (PACK_EXPANSION_P (t) || (TREE_CODE (t) == PARM_DECL
+ && TREE_CODE (TREE_TYPE (t)) == TEMPLATE_TYPE_PARM))
+ {
+ *walk_subtrees = 0;
+ return t;
+ }
+ return NULL_TREE;
+}
+
/* Returns true if ARGS (a collection of template arguments) contains
any types that require structural equality testing. */
@@ -27810,6 +27854,12 @@ any_template_arguments_need_structural_equality_p (tree args)
else if (!TYPE_P (arg) && TREE_TYPE (arg)
&& TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (arg)))
return true;
+ /* if dependent instantiation inside trailing return type,
+ require structural comparison, at least until canonical
+ type is made to work for this case */
+ else if (!current_function_decl &&
+ cp_walk_tree_without_duplicates (&arg, find_dependent_parm_decl_r, NULL))
+ return true;
}
}
}
@@ -3932,6 +3932,7 @@ finish_id_expression_1 (tree id_expression,
bool allow_non_integral_constant_expression_p,
bool *non_integral_constant_expression_p,
bool template_p,
+ bool trailing_return_type_p,
bool done,
bool address_p,
bool template_arg_p,
@@ -4007,7 +4008,7 @@ finish_id_expression_1 (tree id_expression,
body, except inside an unevaluated context (i.e. decltype). */
if (TREE_CODE (decl) == PARM_DECL
&& DECL_CONTEXT (decl) == NULL_TREE
- && !cp_unevaluated_operand)
+ && !cp_unevaluated_operand && !trailing_return_type_p)
{
*error_msg = G_("use of parameter outside function body");
return error_mark_node;
@@ -4259,6 +4260,7 @@ finish_id_expression (tree id_expression,
bool allow_non_integral_constant_expression_p,
bool *non_integral_constant_expression_p,
bool template_p,
+ bool trailing_return_type_p,
bool done,
bool address_p,
bool template_arg_p,
@@ -4270,7 +4272,8 @@ finish_id_expression (tree id_expression,
integral_constant_expression_p,
allow_non_integral_constant_expression_p,
non_integral_constant_expression_p,
- template_p, done, address_p, template_arg_p,
+ template_p, trailing_return_type_p,
+ done, address_p, template_arg_p,
error_msg, location);
return result.maybe_add_location_wrapper ();
}
@@ -5737,7 +5740,7 @@ omp_reduction_lookup (location_t loc, tree id, tree type, tree *baselinkp,
if (decl == NULL_TREE)
decl = error_mark_node;
id = finish_id_expression (id, decl, NULL_TREE, &idk, false, true,
- &nonint_cst_expression_p, false, true, false,
+ &nonint_cst_expression_p, false, false, true, false,
false, &error_msg, loc);
if (idk == CP_ID_KIND_UNQUALIFIED
&& identifier_p (id))
new file mode 100644
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++11 } }
+template<unsigned u> struct size_c{ static constexpr unsigned value = u; };
+namespace g {
+template<class T> auto return_size(T t) -> size_c<sizeof(t)>;
+template<class T> auto return_size(T t) -> size_c<sizeof(t)>;
+}
+static_assert(decltype(g::return_size('a'))::value == 1u, "");
new file mode 100644
@@ -0,0 +1,6 @@
+// { dg-do compile { target c++11 } }
+template<bool u> struct bool_c{ static constexpr bool value = u; };
+template<class T> auto noexcepty(T t) -> bool_c<noexcept(t())>;
+template<class T> auto noexcepty(T t) -> bool_c<noexcept(t())>;
+struct foo { void operator()() noexcept; };
+static_assert(decltype(noexcepty(foo{}))::value, "");
new file mode 100644
@@ -0,0 +1,5 @@
+// { dg-do compile { target c++11 } }
+template<unsigned u> struct size_c{ static constexpr unsigned value = u; };
+template<class... T> auto return_size(T... t) -> size_c<sizeof...(t)>;
+template<class... T> auto return_size(T... t) -> size_c<sizeof...(t)>;
+static_assert(decltype(return_size('a'))::value == 1u, "");
new file mode 100644
@@ -0,0 +1,6 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wno-pedantic" }
+template<unsigned u> struct size_c{ static constexpr unsigned value = u; };
+template<class T> auto get_align(T t) -> size_c<alignof(t)>;
+template<class T> auto get_align(T t) -> size_c<alignof(t)>;
+static_assert(decltype(get_align('a'))::value == 1u, "");
new file mode 100644
@@ -0,0 +1,6 @@
+// { dg-do compile { target c++11 } }
+template<bool u> struct bool_c{ static constexpr bool value = u; };
+struct foo { constexpr bool operator()() const { return true; } };
+template<class T> auto bar(T t) -> bool_c<t()>;
+template<class T> auto bar(T t) -> bool_c<t()>;
+static_assert(decltype(bar(foo{}))::value, "");
new file mode 100644
@@ -0,0 +1,18 @@
+// { dg-do compile { target c++11 } }
+template <int N> struct A { static void foo(){} };
+template <> struct A<sizeof(char)> { using foo = int; };
+
+template <class T> void f(T t1) {
+ A<sizeof(t1)>::foo();
+}
+
+template <class T> void g(T t2) {
+ /* if the comparing_specializations check breaks in cp_tree_equal
+ case PARM_DECL, the error will incorrectly report A<sizeof (t1)> */
+ A<sizeof(t2)>::foo(); // { dg-error "dependent-name .A<sizeof .t2.>::foo" }
+}
+
+void h() {
+ f(0);
+ g('0');
+}
new file mode 100644
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++11 } }
+template <int N> struct A { static void foo(){} };
+template <> struct A<sizeof(char)> { using foo = int; };
+
+template<class T1> auto f(T1 t1) -> decltype(A<sizeof(t1)>::foo());
+
+/* if the comparing_specializations check breaks in cp_tree_equal
+case PARM_DECL, the error will incorrectly report A<sizeof (t1)> */
+template<class T2> auto g(T2 t2) -> decltype(A<sizeof(t2)>::foo()); // { dg-error "dependent-name .A<sizeof .t2.>::foo" }
+
+void h() {
+ f(0);
+ g('0'); // { dg-error "no matching function" }
+}
--
2.30.2