The problem here is that some code paths are not prepared to handle a non-dependent PAREN_EXPR, which my fix for PR c++/70106 introduced. In particular lvalue_kind() returns clk_none for a PAREN_EXPR which makes build_x_unary_op() emit a bogus error for an expression like &(A::b). (If the PAREN_EXPR were dependent then lvalue_kind() wouldn't get called in the first place, build_x_unary_op() would exit early.)
This patch replaces the 70106 fix. Instead of wrapping a SCOPE_REF in a PAREN_EXPR, this patch overloads the REF_PARENTHESIZED_P to apply to SCOPE_REFs too. This makes sense to me because the two tree codes are closely related (e.g. a SCOPE_REF before instantiation may become a COMPONENT_REF after instantiation) so they should be treated similarly by force_paren_expr(). There are two rather simpler ways to fix this PR. One is to make lvalue_kind() recurse into PAREN_EXPRs (although other parts of the FE may be mishandling non-dependent PAREN_EXPRs as well), and the other is to make force_paren_expr() never return a non-dependent PAREN_EXPR, which can be achieved by building the PAREN_EXPR with build_nt(). I am not sure which approach is best for GCC 7 and for GCC 6. Somewhat unrelated the fix: I couldn't find an existing test that checked that force_paren_expr handles SCOPE_REFs properly wrt auto deduction so I added one. Bootstrap and regtesting in progress on x86_64-pc-linux-gnu. gcc/cp/ChangeLog: PR c++/70822 PR c++/70106 * cp-tree.h (REF_PARENTHESIZED_P): Make this flag apply to SCOPE_REFs too. * pt.c (tsubst_qualified_id): If REF_PARENTHESIZED_P is set on the qualified_id then propagate it to the resulting expression. (do_auto_deduction): Check REF_PARENTHESIZED_P on SCOPE_REFs too. * semantics.c (force_paren_expr): If given a SCOPE_REF, just set its REF_PARENTHESIZED_P flag. gcc/testsuite/ChangeLog: PR c++/70822 PR c++/70106 * g++.dg/cpp1y/auto-fn31.C: New test. * g++.dg/cpp1y/paren4.C: New test. --- gcc/cp/cp-tree.h | 4 ++-- gcc/cp/pt.c | 15 +++++++++++---- gcc/cp/semantics.c | 13 +++---------- gcc/testsuite/g++.dg/cpp1y/auto-fn31.C | 33 +++++++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp1y/paren4.C | 14 ++++++++++++++ 5 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/auto-fn31.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/paren4.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 2caf7ce..0df5953 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -170,7 +170,7 @@ operator == (const cp_expr &lhs, tree rhs) TARGET_EXPR_DIRECT_INIT_P (in TARGET_EXPR) FNDECL_USED_AUTO (in FUNCTION_DECL) DECLTYPE_FOR_LAMBDA_PROXY (in DECLTYPE_TYPE) - REF_PARENTHESIZED_P (in COMPONENT_REF, INDIRECT_REF) + REF_PARENTHESIZED_P (in COMPONENT_REF, INDIRECT_REF, SCOPE_REF) AGGR_INIT_ZERO_FIRST (in AGGR_INIT_EXPR) CONSTRUCTOR_MUTABLE_POISON (in CONSTRUCTOR) 3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out). @@ -3403,7 +3403,7 @@ extern void decl_shadowed_for_var_insert (tree, tree); some of the time in C++14 mode. */ #define REF_PARENTHESIZED_P(NODE) \ - TREE_LANG_FLAG_2 (TREE_CHECK2 ((NODE), COMPONENT_REF, INDIRECT_REF)) + TREE_LANG_FLAG_2 (TREE_CHECK3 ((NODE), COMPONENT_REF, INDIRECT_REF, SCOPE_REF)) /* Nonzero if this AGGR_INIT_EXPR provides for initialization via a constructor call, rather than an ordinary function call. */ diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index e7ec629..7adf308 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -13741,8 +13741,10 @@ tsubst_qualified_id (tree qualified_id, tree args, { if (is_template) expr = build_min_nt_loc (loc, TEMPLATE_ID_EXPR, expr, template_args); - return build_qualified_name (NULL_TREE, scope, expr, - QUALIFIED_NAME_IS_TEMPLATE (qualified_id)); + tree r = build_qualified_name (NULL_TREE, scope, expr, + QUALIFIED_NAME_IS_TEMPLATE (qualified_id)); + REF_PARENTHESIZED_P (r) = REF_PARENTHESIZED_P (qualified_id); + return r; } if (!BASELINK_P (name) && !DECL_P (expr)) @@ -13822,6 +13824,9 @@ tsubst_qualified_id (tree qualified_id, tree args, && TREE_CODE (expr) != OFFSET_REF) expr = convert_from_reference (expr); + if (REF_PARENTHESIZED_P (qualified_id)) + expr = force_paren_expr (expr); + return expr; } @@ -23966,8 +23971,10 @@ do_auto_deduction (tree type, tree init, tree auto_node, if (AUTO_IS_DECLTYPE (auto_node)) { - bool id = (DECL_P (init) || (TREE_CODE (init) == COMPONENT_REF - && !REF_PARENTHESIZED_P (init))); + bool id = (DECL_P (init) + || ((TREE_CODE (init) == COMPONENT_REF + || TREE_CODE (init) == SCOPE_REF) + && !REF_PARENTHESIZED_P (init))); targs = make_tree_vec (1); TREE_VEC_ELT (targs, 0) = finish_decltype_type (init, id, tf_warning_or_error); diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index bb13fad..89da773 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -1647,17 +1647,10 @@ force_paren_expr (tree expr) && TREE_CODE (expr) != SCOPE_REF) return expr; - if (TREE_CODE (expr) == COMPONENT_REF) + if (TREE_CODE (expr) == COMPONENT_REF + || TREE_CODE (expr) == SCOPE_REF) REF_PARENTHESIZED_P (expr) = true; - else if (type_dependent_expression_p (expr) - /* When processing_template_decl, a SCOPE_REF may actually be - referring to a non-static data member of the current class, in - which case its TREE_TYPE may not be properly cv-qualified (the - cv-qualifiers of the implicit *this object haven't yet been taken - into account) so we have to delay building a static_cast until - instantiation. */ - || (processing_template_decl - && TREE_CODE (expr) == SCOPE_REF)) + else if (type_dependent_expression_p (expr)) expr = build1 (PAREN_EXPR, TREE_TYPE (expr), expr); else if (VAR_P (expr) && DECL_HARD_REGISTER (expr)) /* We can't bind a hard register variable to a reference. */; diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn31.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn31.C new file mode 100644 index 0000000..0a5dafc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn31.C @@ -0,0 +1,33 @@ +// { dg-do compile { target c++14 } } + +template<class,class> struct same_type; +template<class T> struct same_type<T,T> {}; + +struct A +{ + static int b; + int c; + + template <int> + decltype(auto) f() { return A::c; } + + template <int> + decltype(auto) g() { return (A::c); } +}; + +A a; + +template <int> +decltype(auto) f() { return A::b; } + +template <int> +decltype(auto) g() { return (A::b); } + +int main() +{ + same_type<decltype(f<0>()), int>(); + same_type<decltype(g<0>()), int&>(); + + same_type<decltype(a.f<0>()), int>(); + same_type<decltype(a.g<0>()), int&>(); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/paren4.C b/gcc/testsuite/g++.dg/cpp1y/paren4.C new file mode 100644 index 0000000..71abe84 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/paren4.C @@ -0,0 +1,14 @@ +// PR c++/70822 +// { dg-do compile { target c++14 } } + +struct a +{ + static int b; +}; + +template <typename> +void +foo () +{ + &(a::b); +} -- 2.8.1.361.g2fbef4c