From 6fc9f7aa731e895585c47d740509b5cd1591e797 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 26 May 2017 11:17:54 +0200 Subject: [PATCH] cp-tree.h (struct lang_decl_decomp): New type. * cp-tree.h (struct lang_decl_decomp): New type. (struct lang_decl): Add u.decomp. (LANG_DECL_DECOMP_CHECK): Define. (DECL_DECOMPOSITION_P): Note it is set also on the vars for user identifiers. (DECL_DECOMP_BASE): Define. (retrofit_lang_decl): Add extra int = 0 argument. * lex.c (retrofit_lang_decl): Add SEL argument, if non-zero use it to influence the selector choices and for selector 0 to non-zero transition copy old content. (cxx_dup_lang_specific_decl): Handle DECL_DECOMPOSITION_P. * decl.c (poplevel): For DECL_DECOMPOSITION_P, check !DECL_DECOMP_BASE instead of !DECL_VALUE_EXPR. Adjust warning wording if decl is a structured binding. (cp_finish_decomp): Pass 4 as the new argument to retrofit_lang_decl. Set DECL_DECOMP_BASE. Ignore DECL_READ_P sets from initialization of individual variables for tuple structured bindings. (grokdeclarator): Pass 4 as the new argument to retrofit_lang_decl. Clear DECL_DECOMP_BASE. * decl2.c (mark_used): Mark DECL_DECOMP_BASE TREE_USED as well. * pt.c (tsubst_decomp_names): Assert DECL_DECOMP_BASE matches what is expected. * expr.c (mark_exp_read): Recurse on DECL_DECOMP_BASE instead of DECL_VALUE_EXPR. * g++.dg/cpp1z/decomp29.C (p): New variable. (main): Add further tests. From-SVN: r248483 --- gcc/cp/ChangeLog | 27 +++++++++++++++++++ gcc/cp/cp-tree.h | 30 ++++++++++++++++++--- gcc/cp/decl.c | 38 ++++++++++++++++++++------- gcc/cp/decl2.c | 3 +++ gcc/cp/expr.c | 4 +-- gcc/cp/lex.c | 24 ++++++++++++++--- gcc/cp/pt.c | 1 + gcc/testsuite/ChangeLog | 5 ++++ gcc/testsuite/g++.dg/cpp1z/decomp29.C | 36 ++++++++++++++++++++++--- 9 files changed, 147 insertions(+), 21 deletions(-) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 9b7b58651f8..8143e8902ac 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,30 @@ +2017-05-26 Jakub Jelinek + + * cp-tree.h (struct lang_decl_decomp): New type. + (struct lang_decl): Add u.decomp. + (LANG_DECL_DECOMP_CHECK): Define. + (DECL_DECOMPOSITION_P): Note it is set also on the vars + for user identifiers. + (DECL_DECOMP_BASE): Define. + (retrofit_lang_decl): Add extra int = 0 argument. + * lex.c (retrofit_lang_decl): Add SEL argument, if non-zero + use it to influence the selector choices and for selector + 0 to non-zero transition copy old content. + (cxx_dup_lang_specific_decl): Handle DECL_DECOMPOSITION_P. + * decl.c (poplevel): For DECL_DECOMPOSITION_P, check + !DECL_DECOMP_BASE instead of !DECL_VALUE_EXPR. Adjust warning + wording if decl is a structured binding. + (cp_finish_decomp): Pass 4 as the new argument to retrofit_lang_decl. + Set DECL_DECOMP_BASE. Ignore DECL_READ_P sets from initialization + of individual variables for tuple structured bindings. + (grokdeclarator): Pass 4 as the new argument to retrofit_lang_decl. + Clear DECL_DECOMP_BASE. + * decl2.c (mark_used): Mark DECL_DECOMP_BASE TREE_USED as well. + * pt.c (tsubst_decomp_names): Assert DECL_DECOMP_BASE matches what + is expected. + * expr.c (mark_exp_read): Recurse on DECL_DECOMP_BASE instead of + DECL_VALUE_EXPR. + 2017-05-25 Jason Merrill PR c++/80605 - __is_standard_layout and zero-length array diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 514cb890846..a471a639742 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2516,6 +2516,15 @@ struct GTY(()) lang_decl_parm { int index; }; +/* Additional DECL_LANG_SPECIFIC information for structured bindings. */ + +struct GTY(()) lang_decl_decomp { + struct lang_decl_min min; + /* The artificial underlying "e" variable of the structured binding + variable. */ + tree base; +}; + /* DECL_LANG_SPECIFIC for all types. It would be nice to just make this a union rather than a struct containing a union as its only field, but tree.h declares it as a struct. */ @@ -2527,6 +2536,7 @@ struct GTY(()) lang_decl { struct lang_decl_fn GTY ((tag ("1"))) fn; struct lang_decl_ns GTY((tag ("2"))) ns; struct lang_decl_parm GTY((tag ("3"))) parm; + struct lang_decl_decomp GTY((tag ("4"))) decomp; } u; }; @@ -2563,6 +2573,13 @@ struct GTY(()) lang_decl { lang_check_failed (__FILE__, __LINE__, __FUNCTION__); \ <->u.parm; }) +#define LANG_DECL_DECOMP_CHECK(NODE) __extension__ \ +({ struct lang_decl *lt = DECL_LANG_SPECIFIC (NODE); \ + if (!VAR_P (NODE) \ + || lt->u.base.selector != 4) \ + lang_check_failed (__FILE__, __LINE__, __FUNCTION__); \ + <->u.decomp; }) + #define LANG_DECL_U2_CHECK(NODE, TF) __extension__ \ ({ struct lang_decl *lt = DECL_LANG_SPECIFIC (NODE); \ if (!LANG_DECL_HAS_MIN (NODE) || lt->u.base.u2sel != TF) \ @@ -2583,6 +2600,9 @@ struct GTY(()) lang_decl { #define LANG_DECL_PARM_CHECK(NODE) \ (&DECL_LANG_SPECIFIC (NODE)->u.parm) +#define LANG_DECL_DECOMP_CHECK(NODE) \ + (&DECL_LANG_SPECIFIC (NODE)->u.decomp) + #define LANG_DECL_U2_CHECK(NODE, TF) \ (&DECL_LANG_SPECIFIC (NODE)->u.min.u2) @@ -3816,8 +3836,8 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))->u.base.var_declared_inline_p \ = true) -/* Nonzero if NODE is an artificial VAR_DECL for a C++17 decomposition - declaration. */ +/* Nonzero if NODE is an artificial VAR_DECL for a C++17 structured binding + declaration or one of VAR_DECLs for the user identifiers in it. */ #define DECL_DECOMPOSITION_P(NODE) \ (VAR_P (NODE) && DECL_LANG_SPECIFIC (NODE) \ ? DECL_LANG_SPECIFIC (NODE)->u.base.decomposition_p \ @@ -3826,6 +3846,10 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))->u.base.decomposition_p \ = true) +/* The underlying artificial VAR_DECL for structured binding. */ +#define DECL_DECOMP_BASE(NODE) \ + (LANG_DECL_DECOMP_CHECK (NODE)->base) + /* Nonzero if NODE is an inline VAR_DECL. In C++17, static data members declared with constexpr specifier are implicitly inline variables. */ #define DECL_INLINE_VAR_P(NODE) \ @@ -6261,7 +6285,7 @@ extern tree unqualified_name_lookup_error (tree, extern tree unqualified_fn_lookup_error (cp_expr); extern tree build_lang_decl (enum tree_code, tree, tree); extern tree build_lang_decl_loc (location_t, enum tree_code, tree, tree); -extern void retrofit_lang_decl (tree); +extern void retrofit_lang_decl (tree, int = 0); extern tree copy_decl (tree CXX_MEM_STAT_INFO); extern tree copy_type (tree CXX_MEM_STAT_INFO); extern tree cxx_make_type (enum tree_code); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 3ff0130cc64..59cb315d8e0 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -658,7 +658,7 @@ poplevel (int keep, int reverse, int functionbody) && ! DECL_IN_SYSTEM_HEADER (decl) /* For structured bindings, consider only real variables, not subobjects. */ - && (DECL_DECOMPOSITION_P (decl) ? !DECL_VALUE_EXPR (decl) + && (DECL_DECOMPOSITION_P (decl) ? !DECL_DECOMP_BASE (decl) : (DECL_NAME (decl) && !DECL_ARTIFICIAL (decl))) && type != error_mark_node && (!CLASS_TYPE_P (type) @@ -667,16 +667,28 @@ poplevel (int keep, int reverse, int functionbody) TYPE_ATTRIBUTES (TREE_TYPE (decl))))) { if (! TREE_USED (decl)) - warning_at (DECL_SOURCE_LOCATION (decl), - OPT_Wunused_variable, "unused variable %qD", decl); + { + if (!DECL_NAME (decl) && DECL_DECOMPOSITION_P (decl)) + warning_at (DECL_SOURCE_LOCATION (decl), + OPT_Wunused_variable, + "unused structured binding declaration"); + else + warning_at (DECL_SOURCE_LOCATION (decl), + OPT_Wunused_variable, "unused variable %qD", decl); + } else if (DECL_CONTEXT (decl) == current_function_decl // For -Wunused-but-set-variable leave references alone. && TREE_CODE (TREE_TYPE (decl)) != REFERENCE_TYPE && errorcount == unused_but_set_errorcount) { - warning_at (DECL_SOURCE_LOCATION (decl), - OPT_Wunused_but_set_variable, - "variable %qD set but not used", decl); + if (!DECL_NAME (decl) && DECL_DECOMPOSITION_P (decl)) + warning_at (DECL_SOURCE_LOCATION (decl), + OPT_Wunused_but_set_variable, "structured " + "binding declaration set but not used"); + else + warning_at (DECL_SOURCE_LOCATION (decl), + OPT_Wunused_but_set_variable, + "variable %qD set but not used", decl); unused_but_set_errorcount = errorcount; } } @@ -7361,8 +7373,9 @@ cp_finish_decomp (tree decl, tree first, unsigned int count) } if (processing_template_decl) { - retrofit_lang_decl (first); + retrofit_lang_decl (first, 4); SET_DECL_DECOMPOSITION_P (first); + DECL_DECOMP_BASE (first) = decl; } first = DECL_CHAIN (first); } @@ -7375,8 +7388,9 @@ cp_finish_decomp (tree decl, tree first, unsigned int count) for (unsigned int i = 0; i < count; i++, d = DECL_CHAIN (d)) { v[count - i - 1] = d; - retrofit_lang_decl (d); + retrofit_lang_decl (d, 4); SET_DECL_DECOMPOSITION_P (d); + DECL_DECOMP_BASE (d) = decl; } tree type = TREE_TYPE (decl); @@ -7482,6 +7496,7 @@ cp_finish_decomp (tree decl, tree first, unsigned int count) eltscnt = tree_to_uhwi (tsize); if (count != eltscnt) goto cnt_mismatch; + int save_read = DECL_READ_P (decl); for (unsigned i = 0; i < count; ++i) { location_t sloc = input_location; @@ -7514,6 +7529,10 @@ cp_finish_decomp (tree decl, tree first, unsigned int count) cp_finish_decl (v[i], init, /*constexpr*/false, /*asm*/NULL_TREE, LOOKUP_NORMAL); } + /* Ignore reads from the underlying decl performed during initialization + of the individual variables. If those will be read, we'll mark + the underlying decl as read at that point. */ + DECL_READ_P (decl) = save_read; } else if (TREE_CODE (type) == UNION_TYPE) { @@ -12295,9 +12314,10 @@ grokdeclarator (const cp_declarator *declarator, { gcc_assert (declarator && declarator->kind == cdk_decomp); DECL_SOURCE_LOCATION (decl) = declarator->id_loc; - retrofit_lang_decl (decl); + retrofit_lang_decl (decl, 4); DECL_ARTIFICIAL (decl) = 1; SET_DECL_DECOMPOSITION_P (decl); + DECL_DECOMP_BASE (decl) = NULL_TREE; } } diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 85310e0e666..73eeb01fd75 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -5027,6 +5027,9 @@ mark_used (tree decl, tsubst_flags_t complain) /* Set TREE_USED for the benefit of -Wunused. */ TREE_USED (decl) = 1; + /* And for structured bindings also the underlying decl. */ + if (DECL_DECOMPOSITION_P (decl) && DECL_DECOMP_BASE (decl)) + TREE_USED (DECL_DECOMP_BASE (decl)) = 1; if (TREE_CODE (decl) == TEMPLATE_DECL) return true; diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c index 75e99e598af..8bd341b814e 100644 --- a/gcc/cp/expr.c +++ b/gcc/cp/expr.c @@ -133,8 +133,8 @@ mark_exp_read (tree exp) switch (TREE_CODE (exp)) { case VAR_DECL: - if (DECL_VALUE_EXPR (exp)) - mark_exp_read (DECL_VALUE_EXPR (exp)); + if (DECL_DECOMPOSITION_P (exp)) + mark_exp_read (DECL_DECOMP_BASE (exp)); gcc_fallthrough (); case PARM_DECL: DECL_READ_P (exp) = 1; diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c index 75dc15910b0..a9c38ff24a3 100644 --- a/gcc/cp/lex.c +++ b/gcc/cp/lex.c @@ -529,16 +529,28 @@ build_lang_decl_loc (location_t loc, enum tree_code code, tree name, tree type) and pushdecl (for functions generated by the back end). */ void -retrofit_lang_decl (tree t) +retrofit_lang_decl (tree t, int sel) { struct lang_decl *ld; size_t size; - int sel; + size_t oldsize = 0; if (DECL_LANG_SPECIFIC (t)) - return; + { + if (sel) + { + if (DECL_LANG_SPECIFIC (t)->u.base.selector == sel) + return; + gcc_assert (DECL_LANG_SPECIFIC (t)->u.base.selector == 0); + oldsize = sizeof (struct lang_decl_min); + } + else + return; + } - if (TREE_CODE (t) == FUNCTION_DECL) + if (sel == 4) + size = sizeof (struct lang_decl_decomp); + else if (TREE_CODE (t) == FUNCTION_DECL) sel = 1, size = sizeof (struct lang_decl_fn); else if (TREE_CODE (t) == NAMESPACE_DECL) sel = 2, size = sizeof (struct lang_decl_ns); @@ -550,6 +562,8 @@ retrofit_lang_decl (tree t) gcc_unreachable (); ld = (struct lang_decl *) ggc_internal_cleared_alloc (size); + if (oldsize) + memcpy (ld, DECL_LANG_SPECIFIC (t), oldsize); ld->u.base.selector = sel; @@ -584,6 +598,8 @@ cxx_dup_lang_specific_decl (tree node) size = sizeof (struct lang_decl_ns); else if (TREE_CODE (node) == PARM_DECL) size = sizeof (struct lang_decl_parm); + else if (DECL_DECOMPOSITION_P (node)) + size = sizeof (struct lang_decl_decomp); else if (LANG_DECL_HAS_MIN (node)) size = sizeof (struct lang_decl_min); else diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 984961b7af2..306838550a6 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -15705,6 +15705,7 @@ tsubst_decomp_names (tree decl, tree pattern_decl, tree args, return error_mark_node; } (*cnt)++; + gcc_assert (DECL_DECOMP_BASE (decl2) == pattern_decl); gcc_assert (DECL_HAS_VALUE_EXPR_P (decl2)); tree v = DECL_VALUE_EXPR (decl2); DECL_HAS_VALUE_EXPR_P (decl2) = 0; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6042b1902ef..4f73955c6f6 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-05-26 Jakub Jelinek + + * g++.dg/cpp1z/decomp29.C (p): New variable. + (main): Add further tests. + 2017-05-26 Richard Biener PR tree-optimization/80842 diff --git a/gcc/testsuite/g++.dg/cpp1z/decomp29.C b/gcc/testsuite/g++.dg/cpp1z/decomp29.C index daf07a038d7..3ccc3839580 100644 --- a/gcc/testsuite/g++.dg/cpp1z/decomp29.C +++ b/gcc/testsuite/g++.dg/cpp1z/decomp29.C @@ -5,6 +5,7 @@ struct A { int i,j,k; }; A f(); +int p[3]; int z; @@ -13,14 +14,43 @@ int main() { auto [i,j,k] = f(); // { dg-warning "unused" } } + { + [[maybe_unused]] auto [i,j,k] = f(); + } { auto [i,j,k] = f(); z = i; } + { + auto [i,j,k] = f(); // { dg-warning "unused" } + i = 5; + } { auto [i,j] = std::tuple{1,2}; // { dg-warning "unused" } } - // No parallel second test, because in this case i and j are variables rather - // than mere bindings, so there isn't a link between them and using i will - // not prevent a warning about unused j. + { + [[maybe_unused]] auto [i,j] = std::tuple{1,2}; + } + { + auto [i,j] = std::tuple{1,2}; + z = i; + } + { + auto [i,j] = std::tuple{1,2}; + i = 5; + } + { + auto [i,j,k] = p; // { dg-warning "unused" } + } + { + [[maybe_unused]] auto [i,j,k] = p; + } + { + auto [i,j,k] = p; + z = i; + } + { + auto [i,j,k] = p; // { dg-warning "unused" } + i = 5; + } } -- 2.30.2