diff mbox

[WIP,C++] P0217R3 - C++17 structured bindings

Message ID 20161109122422.GX3541@tucnak.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek Nov. 9, 2016, 12:24 p.m. UTC
Hi!

The following patch is a WIP on P0217R3 - decomposition declarations.
It contains various FIXMEs, Jason, do you think you could finish it up?

The most important unfinished part in the patch is that cp_finish_decomp
for classes doesn't try to check if std::tuple_size<type>::value is
well-formed integral constant expression and use decl.get<i>() or
get<i>(decl) as initializers and std::tuple_element<i, type>::type
as type of the individual vars (that need cp_finish_decl then and
dropping of DEC_HAS_VALUE_EXPR_P/DECL_VALUE_EXPR if they have any)
- template-ids, lookups, instantiations isn't something I'm comfortable
enough with to write that.

Another thing is with the non-reference decompositions - I think
int arr[2];
...
auto [ x, y ] = arr;
works properly as copy-initialization, avoids explicit copy constructors,
as I've tried to test in the decomp6.C, though the templates used during
the VEC_INIT_EXPR gimplification aren't actually instantiated (see the
fixme routine in the test that really shouldn't be necessary).
But
auto [ x, y ] { arr };
doesn't seem to work correctly, see the commented out part of decomp6.C,
the cp-gimplify.c hunk is kind of hackish, but it seems to try to
use A::A<A [6]> conversion ctor while I believe it should use the
A::A(const A &) copy ctor.

Another thing is in find_decomp_class_base - I think the current code
will just reject if the same base class with any non-static data members
appears as virtual base in more than one place in the bases tree,
not really sure what should be done, how to check if the paths to
that base are accessible etc.

Another thing is in find_decomp_class_base caller, while
that function can return some base class if the only non-static data
members are in such a base and not in the derived class(es), the caller
isn't prepared to expand that, not sure if we need build_base_path or what
to actually generate the COMPONENT_REFs for the artificial FIELD_DECLs
or what.

Not sure about how the qualifiers from class fields and from the
cv-qualifiers on the decomposition declaration should be treated, at the
moment I'm oring them in.

The match.pd hunk is needed, otherwise the generic folding happily folds
int arr[2];
...
auto [ x, y ] = arr;
&x == &arr[0]
into 0, because it thinks x and arr are distinct VAR_DECLs.  Though, if
such comparisons are required to be folded in constexpr contexts under
certain conditions, we'd need to handle the DECL_VALUE_EXPRs in constexpr.c
somehow.

Mangling of the decomposition declaration base decls at namespace scope
isn't implemented; I think the DECL_ASSEMBLER_NAME for them (they have
NULL DECL_NAME) could be done in cp_finish_decomp, where we have all the
corresponding identifiers available, but it would probably need to be done
through direct calls to the demangler, as the base decl isn't all that is
needed for that.

Per IRC discussions, the associated VAR_DECLs are chained through
DECL_CHAIN.  It isn't that easy though, because decls are pushed into the
bindings in reverse order and afterwards nreversed.  Plus at least for
the (not yet implemented) std::tuple_size<E>::value stuff where the
VAR_DECLs shouldn't have DECL_VALUE_EXPR, the desired final order is
that the nameless artificial base decl comes first and then the other
decls in the order they appear.  But that means that at cp_finish_decl
time of the base decl they are actually in the order y, x, D.1234
for tha above auto [ x, y ] = arr; - so the finalizing of those is done
by separate cp_finish_decomp that needs to know the start of the chain
(the last named variable of the decomposition) plus the base artificial
decl and count.  For range for it uses the DECL_VALUE_EXPRs to find
those, maybe it would be better to pass around during parsing not just
address of a tree (the range decl), but address of a triplet (range decl, first
decomp decl in the chain (i.e. last one) and count, so that range for
parsing doesn't have to rediscover those.  The base decl has the
DECL_DECOMPOSITION_P flag on it set and NULL_TREE DECL_NAME, the
instantiation code in the patch doesn't parse DECL_VALUE_EXPRs though,
because if the decomp initializer is not type dependent, those
DECL_VALUE_EXPRs aren't actually in a standardized form that could be
easily parseable, so it just assumes those named decls have also
DECL_DECOMPOSITION_P flag on and non-NULL DECL_NAME and follow
the base decl in DECL_CHAIN (the new ones created with tsubst_expr
in reverse order again).  If nothing attempts to fold stuff in templates,
perhaps we could avoid setting DECL_VALUE_EXPRs at all when
processing_template_decl?


	Jakub

Comments

Jakub Jelinek Nov. 9, 2016, 1:05 p.m. UTC | #1
Hi!

On Wed, Nov 09, 2016 at 01:24:22PM +0100, Jakub Jelinek wrote:
> The following patch is a WIP on P0217R3 - decomposition declarations.
> It contains various FIXMEs, Jason, do you think you could finish it up?

Some more comments:

Invalid? code like
int arr[2];
extern int x, y;
auto [ x, y ] = arr;
depends on PR78217 fix, so I haven't added testsuite coverage for that yet.
Nor for decomp at namespace scope.  There is no coverage for bitfields
either.

And the testsuite coverage surely needs to have some verification of the
exact types and cv quals of the individual decls, the tests only cover
their addresses.  As implemented in the patch, those decls with
DECL_VALUE_EXPRs have non-reference type always, not sure if it is ok
or not.

	Jakub
diff mbox

Patch

--- gcc/match.pd.jj	2016-11-07 18:32:56.000000000 +0100
+++ gcc/match.pd	2016-11-08 14:00:05.391773322 +0100
@@ -2547,8 +2547,15 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     (with
      {
        int equal = 2;
-       if (decl_in_symtab_p (base0)
-	   && decl_in_symtab_p (base1))
+       /* Punt in GENERIC on variables with value expressions;
+	  the value expressions might point to fields/elements
+	  of other vars etc.  */
+       if (GENERIC
+	   && ((VAR_P (base0) && DECL_HAS_VALUE_EXPR_P (base0))
+	       || (VAR_P (base1) && DECL_HAS_VALUE_EXPR_P (base1))))
+	 ;
+       else if (decl_in_symtab_p (base0)
+		&& decl_in_symtab_p (base1))
          equal = symtab_node::get_create (base0)
 	           ->equal_address_to (symtab_node::get_create (base1));
        else if ((DECL_P (base0)
--- gcc/cp/cp-tree.h.jj	2016-11-07 18:20:30.295762920 +0100
+++ gcc/cp/cp-tree.h	2016-11-09 12:11:58.088010491 +0100
@@ -2228,7 +2228,8 @@  struct GTY(()) lang_decl_base {
   unsigned u2sel : 1;
   unsigned concept_p : 1;                  /* applies to vars and functions */
   unsigned var_declared_inline_p : 1;	   /* var */
-  /* 2 spare bits */
+  unsigned decomposition_p : 1;		   /* var */
+  /* 1 spare bit */
 };
 
 /* True for DECL codes which have template info and access.  */
@@ -3626,6 +3627,16 @@  more_aggr_init_expr_args_p (const aggr_i
   (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))->u.base.var_declared_inline_p \
    = true)
 
+/* Nonzero if NODE is the artificial VAR_DECL for decomposition
+   declaration.  */
+#define DECL_DECOMPOSITION_P(NODE) \
+  (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))			\
+   ? DECL_LANG_SPECIFIC (NODE)->u.base.decomposition_p		\
+   : false)
+#define SET_DECL_DECOMPOSITION_P(NODE) \
+  (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))->u.base.decomposition_p \
+   = true)
+
 /* 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) \
@@ -5157,7 +5168,8 @@  enum auto_deduction_context
   adc_unspecified,   /* Not given */
   adc_variable_type, /* Variable initializer deduction */
   adc_return_type,   /* Return type deduction */
-  adc_requirement    /* Argument dedution constraint */
+  adc_requirement,   /* Argument dedution constraint */
+  adc_decomp_type    /* Decomposition declaration initializer deduction */
 };
 
 /* True iff this TEMPLATE_TYPE_PARM represents decltype(auto).  */
@@ -5374,6 +5386,7 @@  enum cp_declarator_kind {
   cdk_pointer,
   cdk_reference,
   cdk_ptrmem,
+  cdk_decomp,
   cdk_error
 };
 
@@ -5404,7 +5417,8 @@  struct cp_declarator {
   /* Whether we parsed an ellipsis (`...') just before the declarator,
      to indicate this is a parameter pack.  */
   BOOL_BITFIELD parameter_pack_p : 1;
-  location_t id_loc; /* Currently only set for cdk_id and cdk_function. */
+  location_t id_loc; /* Currently only set for cdk_id, cdk_decomp and
+			cdk_function. */
   /* GNU Attributes that apply to this declarator.  If the declarator
      is a pointer or a reference, these attribute apply to the type
      pointed to.  */
@@ -5413,8 +5427,8 @@  struct cp_declarator {
      declarator is a pointer or a reference, these attributes apply
      to the pointer, rather than to the type pointed to.  */
   tree std_attributes;
-  /* For all but cdk_id and cdk_error, the contained declarator.  For
-     cdk_id and cdk_error, guaranteed to be NULL.  */
+  /* For all but cdk_id, cdk_decomp and cdk_error, the contained declarator.
+     For cdk_id, cdk_decomp and cdk_error, guaranteed to be NULL.  */
   cp_declarator *declarator;
   union {
     /* For identifiers.  */
@@ -5785,6 +5799,7 @@  extern tree start_decl				(const cp_decl
 extern void start_decl_1			(tree, bool);
 extern bool check_array_initializer		(tree, tree, tree);
 extern void cp_finish_decl			(tree, tree, bool, tree, int);
+extern void cp_finish_decomp			(tree, tree, unsigned int);
 extern int cp_complete_array_type		(tree *, tree, bool);
 extern int cp_complete_array_type_or_error	(tree *, tree, bool, tsubst_flags_t);
 extern tree build_ptrmemfunc_type		(tree);
@@ -6056,7 +6071,7 @@  extern tree implicitly_declare_fn
 extern bool maybe_clone_body			(tree);
 
 /* In parser.c */
-extern tree cp_convert_range_for (tree, tree, tree, bool);
+extern tree cp_convert_range_for (tree, tree, tree, tree, unsigned int, bool);
 extern bool parsing_nsdmi (void);
 extern void inject_this_parameter (tree, cp_cv_quals);
 
--- gcc/cp/parser.c.jj	2016-11-07 18:20:30.298762882 +0100
+++ gcc/cp/parser.c	2016-11-09 12:13:03.868168824 +0100
@@ -1668,6 +1668,7 @@  declarator_can_be_parameter_pack (cp_dec
 	{
 	case cdk_id:
 	case cdk_array:
+	case cdk_decomp:
 	  found = true;
 	  break;
 
@@ -1721,6 +1722,7 @@  function_declarator_p (const cp_declarat
 	  && declarator->declarator->kind == cdk_id)
 	return true;
       if (declarator->kind == cdk_id
+	  || declarator->kind == cdk_decomp
 	  || declarator->kind == cdk_error)
 	return false;
       declarator = declarator->declarator;
@@ -2200,6 +2202,8 @@  static void cp_parser_static_assert
   (cp_parser *, bool);
 static tree cp_parser_decltype
   (cp_parser *);
+static tree cp_parser_decomposition_declaration
+  (cp_parser *, cp_decl_specifier_seq *, tree *, location_t *);
 
 /* Declarators [gram.dcl.decl] */
 
@@ -11445,16 +11449,45 @@  cp_parser_range_for (cp_parser *parser,
 		     bool ivdep)
 {
   tree stmt, range_expr;
-  cxx_binding *binding = NULL;
-  tree name = NULL_TREE;
+  auto_vec <cxx_binding *, 16> bindings;
+  auto_vec <tree, 16> names;
+  tree decomp_first_name = NULL_TREE;
+  unsigned int decomp_cnt = 0;
 
   /* Get the range declaration momentarily out of the way so that
      the range expression doesn't clash with it. */
   if (range_decl != error_mark_node)
     {
-      name = DECL_NAME (range_decl);
-      binding = IDENTIFIER_BINDING (name);
-      IDENTIFIER_BINDING (name) = binding->previous;
+      if (DECL_HAS_VALUE_EXPR_P (range_decl))
+	{
+	  tree v = DECL_VALUE_EXPR (range_decl);
+	  /* For decomposition declaration get all of the corresponding
+	     declarations out of the way.  */
+	  if (TREE_CODE (v) == ARRAY_REF
+	      && VAR_P (TREE_OPERAND (v, 0))
+	      && DECL_DECOMPOSITION_P (TREE_OPERAND (v, 0)))
+	    {
+	      tree d = range_decl;
+	      range_decl = TREE_OPERAND (v, 0);
+	      decomp_cnt = tree_to_uhwi (TREE_OPERAND (v, 1)) + 1;
+	      decomp_first_name = d;
+	      for (unsigned int i = 0; i < decomp_cnt; i++, d = DECL_CHAIN (d))
+		{
+		  tree name = DECL_NAME (d);
+		  names.quick_push (name);
+		  bindings.quick_push (IDENTIFIER_BINDING (name));
+		  IDENTIFIER_BINDING (name)
+		    = IDENTIFIER_BINDING (name)->previous;
+		}
+	    }
+	}
+      if (names.is_empty ())
+	{
+	  tree name = DECL_NAME (range_decl);
+	  names.quick_push (name);
+	  bindings.quick_push (IDENTIFIER_BINDING (name));
+	  IDENTIFIER_BINDING (name) = IDENTIFIER_BINDING (name)->previous;
+	}
     }
 
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
@@ -11465,11 +11498,12 @@  cp_parser_range_for (cp_parser *parser,
   else
     range_expr = cp_parser_expression (parser);
 
-  /* Put the range declaration back into scope. */
-  if (range_decl != error_mark_node)
+  /* Put the range declaration(s) back into scope. */
+  for (unsigned int i = 0; i < names.length (); i++)
     {
-      binding->previous = IDENTIFIER_BINDING (name);
-      IDENTIFIER_BINDING (name) = binding;
+      cxx_binding *binding = bindings[i];
+      binding->previous = IDENTIFIER_BINDING (names[i]);
+      IDENTIFIER_BINDING (names[i]) = binding;
     }
 
   /* If in template, STMT is converted to a normal for-statement
@@ -11490,7 +11524,8 @@  cp_parser_range_for (cp_parser *parser,
   else
     {
       stmt = begin_for_stmt (scope, init);
-      stmt = cp_convert_range_for (stmt, range_decl, range_expr, ivdep);
+      stmt = cp_convert_range_for (stmt, range_decl, range_expr,
+				   decomp_first_name, decomp_cnt, ivdep);
     }
   return stmt;
 }
@@ -11582,6 +11617,7 @@  do_range_for_auto_deduction (tree decl,
 
 tree
 cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
+		      tree decomp_first_name, unsigned int decomp_cnt,
 		      bool ivdep)
 {
   tree begin, end;
@@ -11655,6 +11691,8 @@  cp_convert_range_for (tree statement, tr
 					tf_warning_or_error),
 		  /*is_constant_init*/false, NULL_TREE,
 		  LOOKUP_ONLYCONVERTING);
+  if (VAR_P (range_decl) && DECL_DECOMPOSITION_P (range_decl))
+    cp_finish_decomp (range_decl, decomp_first_name, decomp_cnt);
 
   return statement;
 }
@@ -12528,6 +12566,8 @@  cp_parser_block_declaration (cp_parser *
 
    simple-declaration:
      decl-specifier-seq [opt] init-declarator-list [opt] ;
+     decl-specifier-seq ref-qualifier [opt] [ identifier-list ]
+       brace-or-equal-initializer ;
 
    init-declarator-list:
      init-declarator
@@ -12613,6 +12653,45 @@  cp_parser_simple_declaration (cp_parser*
       && !cp_parser_error_occurred (parser))
     cp_parser_commit_to_tentative_parse (parser);
 
+  /* Look for C++17 decomposition declaration.  */
+  for (size_t n = 1; ; n++)
+    if (cp_lexer_nth_token_is (parser->lexer, n, CPP_AND)
+	|| cp_lexer_nth_token_is (parser->lexer, n, CPP_AND_AND))
+      continue;
+    else if (cp_lexer_nth_token_is (parser->lexer, n, CPP_OPEN_SQUARE)
+	     && !cp_lexer_nth_token_is (parser->lexer, n + 1, CPP_OPEN_SQUARE)
+	     && decl_specifiers.any_specifiers_p)
+      {
+	tree decl
+	  = cp_parser_decomposition_declaration (parser, &decl_specifiers,
+						 maybe_range_for_decl,
+						 &init_loc);
+
+	/* The next token should be either a `,' or a `;'.  */
+	cp_token *token = cp_lexer_peek_token (parser->lexer);
+	/* If it's a `;', we are done.  */
+	if (token->type == CPP_SEMICOLON || maybe_range_for_decl)
+	  goto finish;
+	/* Anything else is an error.  */
+	else
+	  {
+	    /* If we have already issued an error message we don't need
+	       to issue another one.  */
+	    if ((decl != error_mark_node
+		 && DECL_INITIAL (decl) != error_mark_node)
+		|| cp_parser_uncommitted_to_tentative_parse_p (parser))
+	      cp_parser_error (parser, "expected %<,%> or %<;%>");
+	    /* Skip tokens until we reach the end of the statement.  */
+	    cp_parser_skip_to_end_of_statement (parser);
+	    /* If the next token is now a `;', consume it.  */
+	    if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	      cp_lexer_consume_token (parser->lexer);
+	    goto done;
+	  }
+      }
+    else
+      break;
+
   tree last_type;
 
   last_type = NULL_TREE;
@@ -12765,6 +12844,7 @@  cp_parser_simple_declaration (cp_parser*
     }
 
   /* Consume the `;'.  */
+ finish:
   if (!maybe_range_for_decl)
     cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
   else if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
@@ -12780,6 +12860,136 @@  cp_parser_simple_declaration (cp_parser*
   pop_deferring_access_checks ();
 }
 
+/* Helper of cp_parser_simple_declaration, parse a decomposition declaration.
+     decl-specifier-seq ref-qualifier [opt] [ identifier-list ]
+       brace-or-equal-initializer ;  */
+
+static tree
+cp_parser_decomposition_declaration (cp_parser *parser,
+				     cp_decl_specifier_seq *decl_specifiers,
+				     tree *maybe_range_for_decl,
+				     location_t *init_loc)
+{
+  cp_ref_qualifier ref_qual = cp_parser_ref_qualifier_opt (parser);
+  location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+  cp_parser_require (parser, CPP_OPEN_SQUARE, RT_OPEN_SQUARE);
+  if (cxx_dialect < cxx1z)
+    pedwarn (loc, 0, "decomposition declaration only available with "
+		     "-std=c++1z or -std=gnu++1z");
+
+  /* Parse the identifier-list.  */
+  auto_vec<cp_expr, 10> v;
+  if (!cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+    while (true)
+      {
+	cp_expr e = cp_parser_identifier (parser);
+	if (e.get_value () == error_mark_node)
+	  break;
+	v.safe_push (e);
+	if (!cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	  break;
+	cp_lexer_consume_token (parser->lexer);
+      }
+
+  location_t end_loc = cp_lexer_peek_token (parser->lexer)->location;
+  if (!cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE))
+    {
+      end_loc = UNKNOWN_LOCATION;
+      cp_parser_skip_to_closing_parenthesis_1 (parser, true, CPP_CLOSE_SQUARE,
+					       false);
+      if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+	cp_lexer_consume_token (parser->lexer);
+      else
+	{
+	  cp_parser_skip_to_end_of_statement (parser);
+	  return error_mark_node;
+	}
+    }
+
+  tree pushed_scope;
+  cp_declarator *declarator = make_declarator (cdk_decomp);
+  loc = end_loc == UNKNOWN_LOCATION ? loc : make_location (loc, loc, end_loc);
+  declarator->id_loc = loc;
+  if (ref_qual != REF_QUAL_NONE)
+    declarator = make_reference_declarator (TYPE_UNQUALIFIED, declarator,
+					    ref_qual == REF_QUAL_RVALUE,
+					    NULL_TREE);
+  tree decl = start_decl (declarator, decl_specifiers, SD_INITIALIZED,
+			  NULL_TREE, decl_specifiers->attributes,
+			  &pushed_scope);
+
+  unsigned int i;
+  cp_expr e;
+  cp_decl_specifier_seq decl_specs;
+  clear_decl_specs (&decl_specs);
+  decl_specs.type = make_auto ();
+  tree prev = decl;
+  FOR_EACH_VEC_ELT (v, i, e)
+    {
+      if (i == 0)
+	declarator = make_id_declarator (NULL_TREE, e.get_value (), sfk_none);
+      else
+	declarator->u.id.unqualified_name = e.get_value ();
+      declarator->id_loc = e.get_location ();
+      tree decl2 = start_decl (declarator, &decl_specs, SD_INITIALIZED,
+			       NULL_TREE, NULL_TREE, &pushed_scope);
+      if (decl2 == error_mark_node)
+	decl = error_mark_node;
+      else if (decl != error_mark_node && DECL_CHAIN (decl2) != prev)
+	{
+	  /* Ensure we've diagnosed redeclaration if we aren't creating
+	     a new VAR_DECL.  */
+	  gcc_assert (errorcount);
+	  decl = error_mark_node;
+	}
+      else
+	prev = decl2;
+    }
+
+  if (v.is_empty ())
+    {
+      error_at (loc, "empty decomposition declaration");
+      decl = error_mark_node;
+    }
+
+  if (maybe_range_for_decl == NULL
+      || cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
+    {
+      bool non_constant_p = false, is_direct_init = false;
+      tree initializer;
+      *init_loc = cp_lexer_peek_token (parser->lexer)->location;
+      /* Parse the initializer.  */
+      if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+	{
+	  initializer = cp_parser_braced_list (parser, &non_constant_p);
+	  CONSTRUCTOR_IS_DIRECT_INIT (initializer) = 1;
+	  is_direct_init = true;
+	}
+      else
+	{
+	  /* Consume the `='.  */
+	  cp_parser_require (parser, CPP_EQ, RT_EQ);
+	  initializer = cp_parser_initializer_clause (parser, &non_constant_p);
+	}
+
+      if (decl != error_mark_node)
+	{
+	  cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
+			  is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT);
+	  cp_finish_decomp (decl, prev, v.length ());
+	}
+    }
+  else if (decl != error_mark_node)
+    {
+      *maybe_range_for_decl = prev;
+      /* Ensure DECL_VALUE_EXPR is created for all the decls but
+	 the underlying DECL.  */
+      cp_finish_decomp (decl, prev, v.length ());
+    }
+
+  return decl;
+}
+
 /* Parse a decl-specifier-seq.
 
    decl-specifier-seq:
@@ -18587,6 +18797,7 @@  strip_declarator_types (tree type, cp_de
     switch (d->kind)
       {
       case cdk_id:
+      case cdk_decomp:
       case cdk_error:
 	d = NULL;
 	break;
@@ -25447,6 +25658,7 @@  cp_parser_check_declarator_template_para
       return (cp_parser_check_declarator_template_parameters
 	      (parser, declarator->declarator, declarator_location));
 
+    case cdk_decomp:
     case cdk_error:
       return true;
 
--- gcc/cp/decl.c.jj	2016-11-07 18:20:30.299762870 +0100
+++ gcc/cp/decl.c	2016-11-09 12:12:42.372443865 +0100
@@ -6186,6 +6186,15 @@  check_initializer (tree decl, tree init,
       gcc_assert (init != NULL_TREE);
       init = NULL_TREE;
     }
+  else if (VAR_P (decl)
+	   && DECL_DECOMPOSITION_P (decl)
+	   && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+    {
+      init_code = build2 (VEC_INIT_EXPR, type, decl, init);
+      TREE_SIDE_EFFECTS (init_code) = 1;
+      SET_EXPR_LOCATION (init_code, DECL_SOURCE_LOCATION (decl));
+      return init_code;
+    }
   else if (!init && DECL_REALLY_EXTERN (decl))
     ;
   else if (init || type_build_ctor_call (type)
@@ -6762,10 +6771,11 @@  cp_finish_decl (tree decl, tree init, bo
 	d_init = build_x_compound_expr_from_list (d_init, ELK_INIT,
 						  tf_warning_or_error);
       d_init = resolve_nondeduced_context (d_init, tf_warning_or_error);
-      type = TREE_TYPE (decl) = do_auto_deduction (type, d_init,
-						   auto_node,
-                                                   tf_warning_or_error,
-                                                   adc_variable_type);
+      enum auto_deduction_context adc = adc_variable_type;
+      if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
+	adc = adc_decomp_type;
+      type = TREE_TYPE (decl) = do_auto_deduction (type, d_init, auto_node,
+						   tf_warning_or_error, adc);
       if (type == error_mark_node)
 	return;
       if (TREE_CODE (type) == FUNCTION_TYPE)
@@ -7129,6 +7139,281 @@  cp_finish_decl (tree decl, tree init, bo
   invoke_plugin_callbacks (PLUGIN_FINISH_DECL, decl);
 }
 
+/* For class TYPE return itself or some its bases that contain
+   any direct non-static data members.  Return error_mark_node if an
+   error has been diagnosed.  */
+
+static tree
+find_decomp_class_base (location_t loc, tree type, tree ret)
+{
+  bool member_seen = false;
+  for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+    if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
+      continue;
+    else if (ret)
+      return type;
+    else if (ANON_AGGR_TYPE_P (TREE_TYPE (field)))
+      {
+	if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
+	  error_at (loc, "cannot decompose class type %qT because it has an "
+			 "anonymous struct member", type);
+	else
+	  error_at (loc, "cannot decompose class type %qT because it has an "
+			 "anonymous union member", type);
+	inform (DECL_SOURCE_LOCATION (field), "declared here");
+	return error_mark_node;
+      }
+    else if (TREE_PRIVATE (field) || TREE_PROTECTED (field))
+      {
+	error_at (loc, "cannot decompose non-public member %qD of %qT",
+		  field, type);
+	inform (DECL_SOURCE_LOCATION (field),
+		TREE_PRIVATE (field) ? "declared private here"
+		: "declared protected here");
+	return error_mark_node;
+      }
+    else
+      member_seen = true;
+
+  /* FIXME: How to deal with virtual bases etc.?  */
+  tree base_binfo, binfo;
+  tree orig_ret = ret;
+  int i;
+  if (member_seen)
+    ret = type;
+  for (binfo = TYPE_BINFO (type), i = 0;
+       BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+    {
+      tree t = find_decomp_class_base (loc, TREE_TYPE (base_binfo), ret);
+      if (t == error_mark_node)
+	return error_mark_node;
+      if (t != NULL_TREE)
+	{
+	  if (ret == type)
+	    {
+	      error_at (loc, "cannot decompose class type %qT: both it and "
+			     "its base class %qT have non-static data members",
+			type, t);
+	      return error_mark_node;
+	    }
+	  else if (orig_ret != NULL_TREE)
+	    return t;
+	  else if (ret != NULL_TREE)
+	    {
+	      error_at (loc, "cannot decompose class type %qT: its base "
+			     "classes %qT and %qT have non-static data "
+			     "members", type, ret, t);
+	      return error_mark_node;
+	    }
+	  else
+	    /* FIXME: Check if this is thru a private base?  */
+	    ret = t;
+	}
+    }
+  return ret;
+}
+
+/* Finish a decomposition declaration.  DECL is the underlying declaration
+   "e", FIRST is the head of a chain of decls for the individual identifiers
+   chained through DECL_CHAIN in reverse order and COUNT is the number of
+   those decls.  */
+
+void
+cp_finish_decomp (tree decl, tree first, unsigned int count)
+{
+  location_t loc = DECL_SOURCE_LOCATION (decl);
+  if (error_operand_p (decl))
+    {
+     error_out:
+      while (count--)
+	{
+	  TREE_TYPE (first) = error_mark_node;
+	  if (DECL_HAS_VALUE_EXPR_P (first))
+	    {
+	      SET_DECL_VALUE_EXPR (first, NULL_TREE);
+	      DECL_HAS_VALUE_EXPR_P (first) = 0;
+	    }
+	  first = DECL_CHAIN (first);
+	}
+      return;
+    }
+
+  if (type_dependent_expression_p (decl)
+      /* This happens for range for when not in templates.
+	 Still add the DECL_VALUE_EXPRs for later processing.  */
+      || (!processing_template_decl
+	  && type_uses_auto (TREE_TYPE (decl))))
+    {
+      for (unsigned int i = 0; i < count; i++)
+	{
+	  if (!DECL_HAS_VALUE_EXPR_P (first))
+	    {
+	      tree v = build_nt (ARRAY_REF, decl,
+				 size_int (count - i - 1),
+				 NULL_TREE, NULL_TREE);
+	      SET_DECL_VALUE_EXPR (first, v);
+	      DECL_HAS_VALUE_EXPR_P (first) = 1;
+	    }
+	  if (processing_template_decl)
+	    {
+	      retrofit_lang_decl (first);
+	      SET_DECL_DECOMPOSITION_P (first);
+	    }
+	  first = DECL_CHAIN (first);
+	}
+      return;
+    }
+
+  auto_vec<tree, 16> v;
+  v.safe_grow (count);
+  tree d = first;
+  for (unsigned int i = 0; i < count; i++, d = DECL_CHAIN (d))
+    {
+      v[count - i - 1] = d;
+      if (processing_template_decl)
+	{
+	  retrofit_lang_decl (d);
+	  SET_DECL_DECOMPOSITION_P (d);
+	}
+    }
+
+  tree type = TREE_TYPE (decl);
+  tree eltype = NULL_TREE;
+  if (TREE_CODE (type) == REFERENCE_TYPE)
+    type = TREE_TYPE (type);
+
+  unsigned HOST_WIDE_INT eltscnt = 0;
+  if (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      tree nelts;
+      nelts = array_type_nelts_top (type);
+      if (nelts == error_mark_node)
+	goto error_out;
+      if (!tree_fits_uhwi_p (nelts))
+	{
+	  error_at (loc, "cannot decompose variable length array %qT", type);
+	  goto error_out;
+	}
+      eltscnt = tree_to_uhwi (nelts);
+      if (count != eltscnt)
+	{
+       cnt_mismatch:
+	  if (count > eltscnt)
+	    error_at (loc, "%u names provided while %qT decomposes into "
+			   "%wu elements", count, type, eltscnt);
+	  else
+	    error_at (loc, "only %u names provided while %qT decomposes into "
+			   "%wu elements", count, type, eltscnt);
+	  goto error_out;
+	}
+      eltype = TREE_TYPE (type);
+      for (unsigned int i = 0; i < count; i++)
+	{
+	  TREE_TYPE (v[i]) = eltype;
+	  layout_decl (v[i], 0);
+	  tree t = convert_from_reference (decl);
+	  t = build4_loc (DECL_SOURCE_LOCATION (v[i]), ARRAY_REF,
+			  eltype, t, size_int (i), NULL_TREE,
+			  NULL_TREE);
+	  SET_DECL_VALUE_EXPR (v[i], t);
+	  DECL_HAS_VALUE_EXPR_P (v[i]) = 1;
+	}
+    }
+  /* 2 GNU extensions.  */
+  else if (TREE_CODE (type) == COMPLEX_TYPE)
+    {
+      eltscnt = 2;
+      if (count != eltscnt)
+	goto cnt_mismatch;
+      eltype = cp_build_qualified_type (TREE_TYPE (type), TYPE_QUALS (type));
+      for (unsigned int i = 0; i < count; i++)
+	{
+	  TREE_TYPE (v[i]) = eltype;
+	  layout_decl (v[i], 0);
+	  tree t = convert_from_reference (decl);
+	  t = build1_loc (DECL_SOURCE_LOCATION (v[i]),
+			  i ? IMAGPART_EXPR : REALPART_EXPR, eltype,
+			  t);
+	  SET_DECL_VALUE_EXPR (v[i], t);
+	  DECL_HAS_VALUE_EXPR_P (v[i]) = 1;
+	}
+    }
+  else if (TREE_CODE (type) == VECTOR_TYPE)
+    {
+      eltscnt = TYPE_VECTOR_SUBPARTS (type);
+      if (count != eltscnt)
+	goto cnt_mismatch;
+      eltype = cp_build_qualified_type (TREE_TYPE (type), TYPE_QUALS (type));
+      for (unsigned int i = 0; i < count; i++)
+	{
+	  TREE_TYPE (v[i]) = eltype;
+	  layout_decl (v[i], 0);
+	  tree t = convert_from_reference (decl);
+	  convert_vector_to_array_for_subscript (DECL_SOURCE_LOCATION (v[i]),
+						 &t, size_int (i));
+	  t = build4_loc (DECL_SOURCE_LOCATION (v[i]), ARRAY_REF,
+			  eltype, t, size_int (i), NULL_TREE,
+			  NULL_TREE);
+	  SET_DECL_VALUE_EXPR (v[i], t);
+	  DECL_HAS_VALUE_EXPR_P (v[i]) = 1;
+	}
+    }
+  else if (TREE_CODE (type) == UNION_TYPE)
+    {
+      error_at (loc, "cannot decompose union type %qT", type);
+      goto error_out;
+    }
+  else if (!CLASS_TYPE_P (type))
+    {
+      error_at (loc, "cannot decompose non-array non-class type %qT", type);
+      goto error_out;
+    }
+  else
+    {
+      /* FIXME: try to evaluate std::tuple_size<type>::size as integral
+	 constant expression and use std::tuple_element<i, type> as types
+	 and decl.get<i> or get<i>(decl) as initializer.
+	 And only if that fails fall through into this.  */
+      tree btype = find_decomp_class_base (loc, type, NULL_TREE);
+      if (btype == error_mark_node)
+	goto error_out;
+      else if (btype == NULL_TREE)
+	{
+	  error_at (loc, "cannot decompose class type %qT without non-static "
+			 "data members", type);
+	  goto error_out;
+	}
+      for (tree field = TYPE_FIELDS (btype); field; field = TREE_CHAIN (field))
+	if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
+	  continue;
+	else
+	  eltscnt++;
+      if (count != eltscnt)
+	goto cnt_mismatch;
+      tree t = convert_from_reference (decl);
+      /* FIXME: need to build_base_path or something similar here.  */
+      gcc_assert (type == btype);
+      unsigned int i = 0;
+      for (tree field = TYPE_FIELDS (btype); field; field = TREE_CHAIN (field))
+	if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
+	  continue;
+	else
+	  {
+	    tree eltype = unlowered_expr_type (field);
+	    /* FIXME: not really sure about the qualifiers here at all.  */
+	    eltype = cp_build_qualified_type (eltype, TYPE_QUALS (eltype)
+						      | TYPE_QUALS (type));
+	    TREE_TYPE (v[i]) = eltype;
+	    layout_decl (v[i], 0);
+	    tree tt = build3_loc (DECL_SOURCE_LOCATION (v[i]),
+				  COMPONENT_REF, eltype, t, field, NULL_TREE);
+	    SET_DECL_VALUE_EXPR (v[i], tt);
+	    DECL_HAS_VALUE_EXPR_P (v[i]) = 1;
+	    i++;
+	  }
+    }
+}
+
 /* Returns a declaration for a VAR_DECL as if:
 
      extern "C" TYPE NAME;
@@ -9441,7 +9726,7 @@  grokdeclarator (const cp_declarator *dec
   cp_storage_class storage_class;
   bool unsigned_p, signed_p, short_p, long_p, thread_p;
   bool type_was_error_mark_node = false;
-  bool parameter_pack_p = declarator? declarator->parameter_pack_p : false;
+  bool parameter_pack_p = declarator ? declarator->parameter_pack_p : false;
   bool template_type_arg = false;
   bool template_parm_flag = false;
   bool typedef_p = decl_spec_seq_has_spec_p (declspecs, ds_typedef);
@@ -9637,6 +9922,10 @@  grokdeclarator (const cp_declarator *dec
 	case cdk_ptrmem:
 	  break;
 
+	case cdk_decomp:
+	  name = "decomposition";
+	  break;
+
 	case cdk_error:
 	  return error_mark_node;
 
@@ -9846,15 +10135,15 @@  grokdeclarator (const cp_declarator *dec
   if (explicit_intN)
     {
       if (! int_n_enabled_p[declspecs->int_n_idx])
-       {
-         error ("%<__int%d%> is not supported by this target",
-		int_n_data[declspecs->int_n_idx].bitsize);
-         explicit_intN = false;
-       }
+	{
+	  error ("%<__int%d%> is not supported by this target",
+		 int_n_data[declspecs->int_n_idx].bitsize);
+	  explicit_intN = false;
+	}
       else if (pedantic && ! in_system_header_at (input_location))
-       pedwarn (input_location, OPT_Wpedantic,
-                "ISO C++ does not support %<__int%d%> for %qs",
-		int_n_data[declspecs->int_n_idx].bitsize,  name);
+	pedwarn (input_location, OPT_Wpedantic,
+		 "ISO C++ does not support %<__int%d%> for %qs",
+		 int_n_data[declspecs->int_n_idx].bitsize, name);
     }
 
   /* Now process the modifiers that were specified
@@ -10070,6 +10359,79 @@  grokdeclarator (const cp_declarator *dec
       virtualp = 0;
     }
 
+  if (innermost_code == cdk_decomp)
+    {
+      location_t loc = (declarator->kind == cdk_reference
+			? declarator->declarator->id_loc : declarator->id_loc);
+      if (inlinep)
+	error_at (declspecs->locations[ds_inline],
+		  "decomposition declaration cannot be declared %<inline%>");
+      if (typedef_p)
+	error_at (declspecs->locations[ds_typedef],
+		  "decomposition declaration cannot be declared %<typedef%>");
+      if (constexpr_p)
+	error_at (declspecs->locations[ds_constexpr], "decomposition "
+		  "declaration cannot be declared %<constexpr%>");
+      if (thread_p)
+	error_at (declspecs->locations[ds_thread],
+		  "decomposition declaration cannot be declared %qs",
+		  declspecs->gnu_thread_keyword_p
+		  ? "__thread" : "thread_local");
+      if (concept_p)
+	error_at (declspecs->locations[ds_concept],
+		  "decomposition declaration cannot be declared %<concept%>");
+      switch (storage_class)
+	{
+	case sc_none:
+	  break;
+	case sc_register:
+	  error_at (loc, "decomposition declaration cannot be declared "
+		    "%<register%>");
+	  break;
+	case sc_static:
+	  error_at (loc, "decomposition declaration cannot be declared "
+		    "%<static%>");
+	  break;
+	case sc_extern:
+	  error_at (loc, "decomposition declaration cannot be declared "
+		    "%<extern%>");
+	  break;
+	case sc_mutable:
+	  error_at (loc, "decomposition declaration cannot be declared "
+		    "%<mutable%>");
+	  break;
+	case sc_auto:
+	  error_at (loc, "decomposition declaration cannot be declared "
+		    "C++98 %<auto%>");
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      if (TREE_CODE (type) != TEMPLATE_TYPE_PARM
+	  || TYPE_IDENTIFIER (type) != get_identifier ("auto"))
+	{
+	  if (type != error_mark_node)
+	    {
+	      error_at (loc, "decomposition declaration cannot be declared "
+			"with type %qT", type);
+	      inform (loc,
+		      "type must be cv-qualified %<auto%> or reference to "
+		      "cv-qualified %<auto%>");
+	    }
+	  type = build_qualified_type (make_auto (), type_quals);
+	  declspecs->type = type;
+	}
+      inlinep = 0;
+      typedef_p = 0;
+      constexpr_p = 0;
+      thread_p = 0;
+      concept_p = 0;
+      storage_class = sc_none;
+      staticp = 0;
+      declspecs->storage_class = sc_none;
+      declspecs->locations[ds_thread] = UNKNOWN_LOCATION;
+    }
+
   /* Static anonymous unions are dealt with here.  */
   if (staticp && decl_context == TYPENAME
       && declspecs->type
@@ -10209,7 +10571,7 @@  grokdeclarator (const cp_declarator *dec
 					    attr_flags);
 	}
 
-      if (declarator->kind == cdk_id)
+      if (declarator->kind == cdk_id || declarator->kind == cdk_decomp)
 	break;
 
       inner_declarator = declarator->declarator;
@@ -10698,6 +11060,7 @@  grokdeclarator (const cp_declarator *dec
      is non-NULL, we know it is a cdk_id declarator; otherwise, we
      would not have exited the loop above.  */
   if (declarator
+      && declarator->kind == cdk_id
       && declarator->u.id.qualifying_scope
       && MAYBE_CLASS_TYPE_P (declarator->u.id.qualifying_scope))
     {
@@ -10709,13 +11072,14 @@  grokdeclarator (const cp_declarator *dec
 	{
 	  if (friendp)
 	    {
-	      permerror (input_location, "member functions are implicitly friends of their class");
+	      permerror (input_location, "member functions are implicitly "
+					 "friends of their class");
 	      friendp = 0;
 	    }
 	  else
 	    permerror (declarator->id_loc, 
-			  "extra qualification %<%T::%> on member %qs",
-			  ctype, name);
+		       "extra qualification %<%T::%> on member %qs",
+		       ctype, name);
 	}
       else if (/* If the qualifying type is already complete, then we
 		  can skip the following checks.  */
@@ -11088,7 +11452,8 @@  grokdeclarator (const cp_declarator *dec
   else if (unqualified_id == NULL_TREE && decl_context != PARM
 	   && decl_context != CATCHPARM
 	   && TREE_CODE (type) != UNION_TYPE
-	   && ! bitfield)
+	   && ! bitfield
+	   && innermost_code != cdk_decomp)
     {
       error ("abstract declarator %qT used as declaration", type);
       return error_mark_node;
@@ -11673,6 +12038,14 @@  grokdeclarator (const cp_declarator *dec
 
 	if (inlinep)
 	  mark_inline_variable (decl);
+	if (innermost_code == cdk_decomp)
+	  {
+	    gcc_assert (declarator && declarator->kind == cdk_decomp);
+	    DECL_SOURCE_LOCATION (decl) = declarator->id_loc;
+	    retrofit_lang_decl (decl);
+	    DECL_ARTIFICIAL (decl) = 1;
+	    SET_DECL_DECOMPOSITION_P (decl);
+	  }
       }
 
     if (VAR_P (decl) && !initialized)
--- gcc/cp/pt.c.jj	2016-11-07 23:28:25.990267594 +0100
+++ gcc/cp/pt.c	2016-11-09 12:13:26.057884903 +0100
@@ -15244,6 +15244,55 @@  tsubst_find_omp_teams (tree *tp, int *wa
   return NULL_TREE;
 }
 
+/* Helper function for tsubst_expr.  For decomposition declaration
+   artificial base DECL, which is tsubsted PATTERN_DECL, tsubst
+   also the corresponding decls representing the identifiers
+   of the decomposition declaration.  Return DECL if successful
+   or error_mark_node otherwise, set *FIRST to the first decl
+   in the list chained through DECL_CHAIN and *CNT to the number
+   of such decls.  */
+
+static tree
+tsubst_decomp_names (tree decl, tree pattern_decl, tree args,
+		     tsubst_flags_t complain, tree in_decl, tree *first,
+		     unsigned int *cnt)
+{
+  tree decl2, decl3, prev = decl;
+  *cnt = 0;
+  gcc_assert (DECL_NAME (decl) == NULL_TREE);
+  for (decl2 = DECL_CHAIN (pattern_decl);
+       decl2
+       && VAR_P (decl2)
+       && DECL_DECOMPOSITION_P (decl2)
+       && DECL_NAME (decl2);
+       decl2 = DECL_CHAIN (decl2))
+    {
+      (*cnt)++;
+      gcc_assert (DECL_HAS_VALUE_EXPR_P (decl2));
+      tree v = DECL_VALUE_EXPR (decl2);
+      DECL_HAS_VALUE_EXPR_P (decl2) = 0;
+      SET_DECL_VALUE_EXPR (decl2, NULL_TREE);
+      decl3 = tsubst (decl2, args, complain, in_decl);
+      SET_DECL_VALUE_EXPR (decl2, v);
+      DECL_HAS_VALUE_EXPR_P (decl2) = 1;
+      if (VAR_P (decl3))
+	DECL_TEMPLATE_INSTANTIATED (decl3) = 1;
+      maybe_push_decl (decl3);
+      if (error_operand_p (decl3))
+	decl = error_mark_node;
+      else if (decl != error_mark_node
+	       && DECL_CHAIN (decl3) != prev)
+	{
+	  gcc_assert (errorcount);
+	  decl = error_mark_node;
+	}
+      else
+	prev = decl3;
+    }
+  *first = prev;
+  return decl;
+}
+
 /* Like tsubst_copy for expressions, etc. but also does semantic
    processing.  */
 
@@ -15387,6 +15436,16 @@  tsubst_expr (tree t, tree args, tsubst_f
 		      const_init = (DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P
 				    (pattern_decl));
 		    cp_finish_decl (decl, init, const_init, NULL_TREE, 0);
+		    if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
+		      {
+			unsigned int cnt;
+			tree first;
+			decl = tsubst_decomp_names (decl, pattern_decl, args,
+						    complain, in_decl, &first,
+						    &cnt);
+			if (decl != error_mark_node)
+			  cp_finish_decomp (decl, first, cnt);
+		      }
 		  }
 	      }
 	  }
@@ -15414,7 +15473,18 @@  tsubst_expr (tree t, tree args, tsubst_f
         decl = tsubst (decl, args, complain, in_decl);
         maybe_push_decl (decl);
         expr = RECUR (RANGE_FOR_EXPR (t));
-        stmt = cp_convert_range_for (stmt, decl, expr, RANGE_FOR_IVDEP (t));
+	if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
+	  {
+	    unsigned int cnt;
+	    tree first;
+	    decl = tsubst_decomp_names (decl, RANGE_FOR_DECL (t), args,
+					complain, in_decl, &first, &cnt);
+	    stmt = cp_convert_range_for (stmt, decl, expr, first, cnt,
+					 RANGE_FOR_IVDEP (t));
+	  }
+	else
+	  stmt = cp_convert_range_for (stmt, decl, expr, NULL_TREE, 0,
+				       RANGE_FOR_IVDEP (t));
         RECUR (RANGE_FOR_BODY (t));
         finish_for_stmt (stmt);
       }
@@ -24610,7 +24680,15 @@  do_auto_deduction (tree type, tree init,
 
   init = resolve_nondeduced_context (init, complain);
 
-  if (AUTO_IS_DECLTYPE (auto_node))
+  if (context == adc_decomp_type
+      && auto_node == type
+      && init != error_mark_node
+      && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
+    /* [dcl.decomp]/1 - if decomposition declaration has no ref-qualifiers
+       and initializer has array type, deduce cv-qualified array type.  */
+    return cp_build_qualified_type_real (TREE_TYPE (init), TYPE_QUALS (type),
+					 complain);
+  else if (AUTO_IS_DECLTYPE (auto_node))
     {
       bool id = (DECL_P (init)
 		 || ((TREE_CODE (init) == COMPONENT_REF
@@ -24694,6 +24772,7 @@  do_auto_deduction (tree type, tree init,
                     error("placeholder constraints not satisfied");
                     break;
                   case adc_variable_type:
+		  case adc_decomp_type:
                     error ("deduced initializer does not satisfy "
                            "placeholder constraints");
                     break;
@@ -24702,7 +24781,7 @@  do_auto_deduction (tree type, tree init,
                            "placeholder constraints");
                     break;
                   case adc_requirement:
-                    error ("deduced expression type does not saatisy "
+		    error ("deduced expression type does not satisfy "
                            "placeholder constraints");
                     break;
                   }
--- gcc/cp/cp-gimplify.c.jj	2016-10-31 13:28:11.000000000 +0100
+++ gcc/cp/cp-gimplify.c	2016-11-08 23:16:31.839545356 +0100
@@ -617,6 +617,14 @@  cp_gimplify_expr (tree *expr_p, gimple_s
 	int from_array = (init && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE);
 	gcc_assert (EXPR_HAS_LOCATION (*expr_p));
 	input_location = EXPR_LOCATION (*expr_p);
+	if (VAR_P (VEC_INIT_EXPR_SLOT (*expr_p))
+	    && DECL_DECOMPOSITION_P (VEC_INIT_EXPR_SLOT (*expr_p))
+	    && init
+	    && DIRECT_LIST_INIT_P (init)
+	    && CONSTRUCTOR_NELTS (init) == 1
+	    && (TREE_CODE (TREE_TYPE (CONSTRUCTOR_ELT (init, 0)->value))
+		== ARRAY_TYPE))
+	  from_array = 1;
 	*expr_p = build_vec_init (VEC_INIT_EXPR_SLOT (*expr_p), NULL_TREE,
 				  init, VEC_INIT_EXPR_VALUE_INIT (*expr_p),
 				  from_array,
--- gcc/testsuite/g++.dg/cpp1z/decomp1.C.jj	2016-11-08 13:32:55.030786923 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp1.C	2016-11-08 14:02:18.007080438 +0100
@@ -0,0 +1,35 @@ 
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+int a[2] = { 1, 2 };
+struct S { int a; signed char b; float c; } s = { 6, 7, 8.0f };
+
+int
+main ()
+{
+  auto & [ c, d ] = a;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto [ e, f ] = a;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto [ g, h, i ] = s;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto & [ j, k, l ] = s;	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  c++;
+  d++;
+  e += 6;
+  f += 7;
+  g++;
+  h++;
+  j += 10;
+  k += 11;
+  if (c != 2 || &c != &a[0]
+      || d != 3 || &d != &a[1]
+      || e != 7 || &e == &a[0]
+      || f != 9 || &f == &a[1]
+      || g != 7 || &g == &s.a
+      || h != 8 || &h == &s.b
+      || i != 8.0f || &i == &s.c
+      || j != 16 || &j != &s.a
+      || k != 18 || &k != &s.b
+      || l != 8.0f || &l != &s.c
+      || a[0] != 2 || a[1] != 3
+      || s.a != 16 || s.b != 18 || s.c != 8.0f)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp1z/decomp2.C.jj	2016-11-08 14:05:16.422804395 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp2.C	2016-11-08 14:23:35.499806422 +0100
@@ -0,0 +1,54 @@ 
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+typedef int V __attribute__((vector_size (4 * sizeof (int))));
+V a = (V) { 1, 2, 3, 4 };
+__complex__ double b = 5.0 + 6.0i;
+__complex__ int c = 7 + 8i;
+
+int
+main ()
+{
+  auto & [ d, e, f, g ] = a;	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto [ h, i, j, k ] = a;	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto [ l, m ] = b;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto & [ n, o ] = b;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto & [ p, q ] = c;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto [ r, s ] = c;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  d += 10;
+  e += 11;
+  f += 12;
+  g += 13;
+  h += 14;
+  i += 15;
+  j += 16;
+  k += 17;
+  l = l * 2.;
+  m = m * 3.;
+  n = n * 3.;
+  o = o * 2.;
+  p += 18;
+  q += 19;
+  r += 22;
+  s += 23;
+  if (d != 11 || &d != &a[0]
+      || e != 13 || &e != &a[1]
+      || f != 15 || &f != &a[2]
+      || g != 17 || &g != &a[3]
+      || h != 15 || &h == &a[0]
+      || i != 17 || &i == &a[1]
+      || j != 19 || &j == &a[2]
+      || k != 21 || &k == &a[3]
+      || l != 10.0 || &l == &__real__ b
+      || m != 18.0 || &m == &__imag__ b
+      || n != 15.0 || &n != &__real__ b
+      || o != 12.0 || &o != &__imag__ b
+      || p != 25 || &p != &__real__ c
+      || q != 27 || &q != &__imag__ c
+      || r != 29 || &r == &__real__ c
+      || s != 31 || &s == &__imag__ c
+      || a[0] != 11 || a[1] != 13 || a[2] != 15 || a[3] != 17
+      || b != 15.0 + 12.0i
+      || c != 25 + 27i)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp1z/decomp3.C.jj	2016-11-08 14:50:25.944303109 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp3.C	2016-11-08 15:21:44.814703322 +0100
@@ -0,0 +1,66 @@ 
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+struct A { int a, b; float c; };
+A &bar ();
+struct B { int d; };
+B baz ();
+
+void
+test (A &b, B c)
+{
+  int && [ d ] = c;			// { dg-error "decomposition declaration cannot be declared with type 'int'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  char & [ e, f, ff ] { b };		// { dg-error "decomposition declaration cannot be declared with type 'char'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto&[g,h,i]=b;			// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } }
+  decltype (auto) [ j ] = c;		// { dg-error "decomposition declaration cannot be declared with type 'decltype.auto.'" "" { target c++14 } }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+					// { dg-error "expected primary-expression before 'decltype'" "" { target c++11_down } .-2 }
+  auto & & && & [ m, n, o ] = b;	// { dg-error "multiple ref-qualifiers" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  constexpr auto [ p ] = c;		// { dg-error "decomposition declaration cannot be declared 'constexpr'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  friend auto [ q ] = c;		// { dg-error "'friend' used outside of class" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  typedef auto [ r ] = c;		// { dg-error "decomposition declaration cannot be declared 'typedef'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  inline auto [ s ] = c;		// { dg-error "decomposition declaration cannot be declared 'inline'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  __restrict auto [ t ] = c;		// { dg-error "invalid use of 'restrict'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  long long auto [ u ] = c;		// { dg-error "'long long' invalid for 'decomposition'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  virtual auto [ v ] = c;		// { dg-error "'virtual' outside class declaration" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  explicit auto [ w ] = c;		// { dg-error "'explicit' outside class declaration" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  static auto [ x ] = c;		// { dg-error "decomposition declaration cannot be declared 'static'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  extern auto [ y ] { c };		// { dg-error "decomposition declaration cannot be declared 'extern'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+}
+
+void
+test2 (auto & [ p ] = bar ())		// { dg-error "'p' was not declared in this scope" }
+{
+}
+
+int arr[4];
+
+void
+test3 (A &b, B c)
+{
+  auto [ d, e, f ] = arr;		// { dg-error "only 3 names provided while 'int .4.' decomposes into 4 elements" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto & [ g, h, i, j, k ] = arr;	// { dg-error "5 names provided while 'int .4.' decomposes into 4 elements" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto [ l, m ] = b;			// { dg-error "only 2 names provided while 'A' decomposes into 3 elements" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto & [ n, o, p, q ] = b;		// { dg-error "4 names provided while 'A' decomposes into 3 elements" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto [] { c };			// { dg-error "empty decomposition declaration" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto [ r, s ] = c;			// { dg-error "2 names provided while 'B' decomposes into 1 elements" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+}
--- gcc/testsuite/g++.dg/cpp1z/decomp4.C.jj	2016-11-08 15:29:24.769896964 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp4.C	2016-11-08 15:50:58.262587786 +0100
@@ -0,0 +1,32 @@ 
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+struct A { int a; struct { int b; }; };
+struct B { int a; union { int c; long d; }; };
+struct C { int a; private: int b; };
+struct D { int a; private: static int b; };
+struct E { protected: int a; };
+struct F { int a; };
+struct G : public F { int b; };
+struct H { int b; };
+struct I : public F, H {};
+
+void
+test (A &a, B &b, C &c, D &d, E &e, F &f, G &g, H &h, I &i)
+{
+  auto [ j ] = a;			// { dg-error "cannot decompose class type 'A' because it has an anonymous struct member" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto [ k ] { b };			// { dg-error "cannot decompose class type 'B' because it has an anonymous union member" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto [ l ] = c;			// { dg-error "cannot decompose non-public member 'C::b' of 'C'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto [ m ] = d;			// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } }
+  auto [ n ] { e };			// { dg-error "cannot decompose non-public member 'E::a' of 'E'" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto [ o ] { f };			// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } }
+  auto & [ p ] { g };			// { dg-error "cannot decompose class type 'G': both it and its base class 'F' have non-static data members" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+  auto [ q ] { h };			// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } }
+  auto [ r ] { i };			// { dg-error "cannot decompose class type 'I': its base classes 'F' and 'H' have non-static data members" }
+					// { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+}
--- gcc/testsuite/g++.dg/cpp1z/decomp5.C.jj	2016-11-08 17:18:24.312329106 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp5.C	2016-11-08 17:18:18.000000000 +0100
@@ -0,0 +1,40 @@ 
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+struct A { int i; long long j; } a[64];
+
+int
+main ()
+{
+  int i = 0;
+  for (auto &x : a)
+    {
+      x.i = i;
+      x.j = 2 * i++;
+    }
+  for (auto & [ x, y ] : a)	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+    {
+      x += 2;
+      y += 3;
+    }
+  i = 0;
+  for (const auto [ u, v ] : a)	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+    {
+      if (u != i + 2 || v != 2 * i++ + 3)
+	__builtin_abort ();
+    }
+  i = 0;
+  for (auto [ x, y ] : a)	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+    {
+      x += 4;
+      y += 5;
+      if (x != i + 6 || y != 2 * i++ + 8)
+	__builtin_abort ();
+    }
+  i = 0;
+  for (const auto x : a)
+    {
+      if (x.i != i + 2 || x.j != 2 * i++ + 3)
+	__builtin_abort ();
+    }
+}
--- gcc/testsuite/g++.dg/cpp1z/decomp6.C.jj	2016-11-08 18:04:20.612695454 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp6.C	2016-11-09 11:19:15.100450569 +0100
@@ -0,0 +1,103 @@ 
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+int ccnt, dcnt, cccnt, tccnt;
+
+struct A
+{
+  A () : a (6) { ccnt++; }
+  ~A () { dcnt++; }
+  explicit A (const A &x) : a (x.a) { cccnt++; }
+  template <typename T>
+  A (const T &x) : a (x.a) { tccnt++; }
+  int a;
+};
+
+// FIXME: without this dummy function the testcase fails to link.
+void
+fixme ()
+{
+  A x1;
+  A x2 = x1;
+  A x3 { x1 };
+}
+
+int
+main ()
+{
+  if (ccnt || dcnt || cccnt || tccnt)
+    __builtin_abort ();
+  {
+    A a[6];
+    if (ccnt != 6 || dcnt || cccnt || tccnt)
+      __builtin_abort ();
+    {
+      auto [b,c,d,e,f,g] = a;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+      if (ccnt != 6 || dcnt || cccnt || tccnt != 6)
+	__builtin_abort ();
+      b.a++;
+      c.a += 2;
+      f.a += 3;
+      if (b.a != 7 || c.a != 8 || d.a != 6 || e.a != 6 || f.a != 9 || g.a != 6)
+	__builtin_abort ();
+      if (&b == &a[0] || &c == &a[1] || &d == &a[2] || &e == &a[3] || &f == &a[4] || &g == &a[5])
+	__builtin_abort ();
+      {
+	auto&[ h, i, j, k, l, m ] = a;	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+	if (ccnt != 6 || dcnt || cccnt || tccnt != 6)
+	  __builtin_abort ();
+	j.a += 4;
+	k.a += 5;
+	m.a += 6;
+	if (a[0].a != 6 || a[1].a != 6 || a[2].a != 10 || a[3].a != 11 || a[4].a != 6 || a[5].a != 12)
+	  __builtin_abort ();
+	if (&h != &a[0] || &i != &a[1] || &j != &a[2] || &k != &a[3] || &l != &a[4] || &m != &a[5])
+	  __builtin_abort ();
+      }
+      if (ccnt != 6 || dcnt || cccnt || tccnt != 6)
+	__builtin_abort ();
+    }
+    if (ccnt != 6 || dcnt != 6 || cccnt || tccnt != 6)
+      __builtin_abort ();
+  }
+  if (ccnt != 6 || dcnt != 12 || cccnt || tccnt != 6)
+    __builtin_abort ();
+#if 0
+  // FIXME
+  {
+    A a[6];
+    if (ccnt != 12 || dcnt != 12 || cccnt || tccnt != 6)
+      __builtin_abort ();
+    {
+      auto [b,c,d,e,f,g] { a };		// { dag-warning "decomposition declaration only available with" "" { target c++14_down } }
+      if (ccnt != 12 || dcnt != 12 || cccnt != 6 || tccnt != 6)
+	__builtin_abort ();
+      b.a++;
+      c.a += 2;
+      f.a += 3;
+      if (b.a != 7 || c.a != 8 || d.a != 6 || e.a != 6 || f.a != 9 || g.a != 6)
+	__builtin_abort ();
+      if (&b == &a[0] || &c == &a[1] || &d == &a[2] || &e == &a[3] || &f == &a[4] || &g == &a[5])
+	__builtin_abort ();
+      {
+	auto&[ h, i, j, k, l, m ] {a};	// { dag-warning "decomposition declaration only available with" "" { target c++14_down } }
+	if (ccnt != 12 || dcnt != 12 || cccnt != 6 || tccnt != 6)
+	  __builtin_abort ();
+	j.a += 4;
+	k.a += 5;
+	m.a += 6;
+	if (a[0].a != 6 || a[1].a != 6 || a[2].a != 10 || a[3].a != 11 || a[4].a != 6 || a[5].a != 12)
+	  __builtin_abort ();
+	if (&h != &a[0] || &i != &a[1] || &j != &a[2] || &k != &a[3] || &l != &a[4] || &m != &a[5])
+	  __builtin_abort ();
+      }
+      if (ccnt != 12 || dcnt != 12 || cccnt != 6 || tccnt != 6)
+	__builtin_abort ();
+    }
+    if (ccnt != 12 || dcnt != 18 || cccnt != 6 || tccnt != 6)
+      __builtin_abort ();
+  }
+  if (ccnt != 12 || dcnt != 24 || cccnt != 6 || tccnt != 6)
+    __builtin_abort ();
+#endif
+}
--- gcc/testsuite/g++.dg/cpp1z/decomp7.C.jj	2016-11-09 11:19:33.521215800 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp7.C	2016-11-09 11:32:12.191544355 +0100
@@ -0,0 +1,60 @@ 
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+int a[2] = { 1, 2 };
+int b[2] = { 4, 5 };
+struct S { int a; signed char b; float c; } sa = { 6, 7, 8.0f };
+S sb = { 9, 10, 11.0f };
+
+template <typename T, typename U>
+void
+foo (T &x, U &y)
+{
+  auto & [ c, d ] = a;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto [ e, f ] = a;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto [ g, h, i ] = sa;	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto & [ j, k, l ] = sa;	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto & [ m, n ] = x;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto [ o, p ] = x;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto [ q, r, s ] = y;		// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  auto & [ t, u, v ] = y;	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+  c += 1;
+  e += 2;
+  g += 3;
+  j += 4;
+  m += 5;
+  o += 6;
+  q += 7;
+  t += 8;
+  if (c != 2 || &c != &a[0]
+      || d != 2 || &d != &a[1]
+      || e != 3 || &e == &a[0]
+      || f != 2 || &f == &a[1]
+      || g != 9 || &g == &sa.a
+      || h != 7 || &h == &sa.b
+      || i != 8.0f || &i == &sa.c
+      || j != 10 || &j != &sa.a
+      || k != 7 || &k != &sa.b
+      || l != 8.0f || &l != &sa.c
+      || m != 9 || &m != &b[0]
+      || n != 5 || &n != &b[1]
+      || o != 10 || &o == &b[0]
+      || p != 5 || &p == &b[1]
+      || q != 16 || &q == &sb.a
+      || r != 10 || &r == &sb.b
+      || s != 11.0f || &s == &sb.c
+      || t != 17 || &t != &sb.a
+      || u != 10 || &u != &sb.b
+      || v != 11.0f || &v != &sb.c
+      || a[0] != 2 || a[1] != 2
+      || sa.a != 10 || sa.b != 7 || sa.c != 8.0f
+      || b[0] != 9 || b[1] != 5
+      || sb.a != 17 || sb.b != 10 || sb.c != 11.0f)
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  foo (b, sb);
+}
--- gcc/testsuite/g++.dg/cpp1z/decomp8.C.jj	2016-11-09 12:11:07.222661321 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp8.C	2016-11-09 12:10:56.000000000 +0100
@@ -0,0 +1,88 @@ 
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+struct A { int i; long long j; } a[64];
+A b[32];
+
+template <typename T>
+void
+foo (T &b)
+{
+  int i = 0;
+  for (auto &x : a)
+    {
+      x.i = i;
+      x.j = 2 * i++;
+    }
+  for (auto & [ x, y ] : a)	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+    {
+      x += 2;
+      y += 3;
+    }
+  i = 0;
+  for (const auto [ u, v ] : a)	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+    {
+      if (u != i + 2 || v != 2 * i++ + 3)
+	__builtin_abort ();
+    }
+  i = 0;
+  for (auto [ x, y ] : a)	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+    {
+      x += 4;
+      y += 5;
+      if (x != i + 6 || y != 2 * i++ + 8)
+	__builtin_abort ();
+    }
+  i = 0;
+  for (const auto x : a)
+    {
+      if (x.i != i + 2 || x.j != 2 * i++ + 3)
+	__builtin_abort ();
+    }
+  i = 0;
+  for (auto &x : b)
+    {
+      x.i = i;
+      x.j = 2 * i++;
+    }
+  for (auto & [ x, y ] : b)	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+    {
+      x -= 2;
+      y -= 3;
+    }
+  i = 0;
+  for (const auto [ u, v ] : b)	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+    {
+      if (u != i - 2 || v != 2 * i++ - 3)
+	__builtin_abort ();
+    }
+  i = 0;
+  for (auto [ x, y ] : b)	// { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+    {
+      x -= 4;
+      y -= 5;
+      if (x != i - 6 || y != 2 * i++ - 8)
+	__builtin_abort ();
+    }
+  i = 0;
+  for (const auto x : b)
+    {
+      if (x.i != i - 2 || x.j != 2 * i++ - 3)
+	__builtin_abort ();
+    }
+}
+
+int
+main ()
+{
+  foo (b);
+  for (int i = 0; i < 64; i++)
+    {
+      if (a[i].i != i + 2 || a[i].j != 2 * i + 3)
+	__builtin_abort ();
+      if (i >= 32)
+	continue;
+      if (b[i].i != i - 2 || b[i].j != 2 * i - 3)
+	__builtin_abort ();
+    }
+}