Message ID | 20160429155538.13603-1-patrick@parcs.ath.cx |
---|---|
State | New |
Headers | show |
On Fri, Apr 29, 2016 at 11:55 AM, Patrick Palka <patrick@parcs.ath.cx> wrote: > 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 > Ping.
OK. Jason
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); +}