Bug 81176 - decltype(auto) yields reference type for structured binding
Summary: decltype(auto) yields reference type for structured binding
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 7.1.0
: P3 normal
Target Milestone: ---
Assignee: Marek Polacek
URL:
Keywords: wrong-code
Depends on:
Blocks: decltype
  Show dependency treegraph
 
Reported: 2017-06-22 13:51 UTC by Barry Revzin
Modified: 2021-12-03 19:02 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-12-02 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Barry Revzin 2017-06-22 13:51:55 UTC
From StackOverflow (https://stackoverflow.com/q/44698195/2069064), this static assert trips:

#include <type_traits>
#include <tuple>

int main() {
    auto tup = std::make_tuple(1, 2); 
    auto [ a, b ] = tup;
    decltype(auto) c = a;
    static_assert(!std::is_reference<decltype(c)>::value, "!");
}

But c should not be a reference type because decltype(a) is int.
Comment 1 Jakub Jelinek 2017-06-22 16:03:25 UTC
Testcase without any headers:
namespace std {
  template<typename T> struct tuple_size;
  template<int, typename> struct tuple_element;
}

struct A {
  int i;
  template <int I> int& get() { return i; }
};

template<> struct std::tuple_size<A> { static const int value = 2; };
template<int I> struct std::tuple_element<I,A> { using type = int; };

template <class,class> struct same_type;
template <class T> struct same_type<T,T> {};

void
foo (A x)
{
  auto [ a, b ] = x;
  decltype(auto) c = a;
  same_type<decltype(a), int>{};
  same_type<decltype(c), int>{};
}

In finish_decltype_type we have code to handle this:
      /* decltype of a decomposition name drops references in the tuple case
         (unlike decltype of a normal variable) and keeps cv-qualifiers from
         the containing object in the other cases (unlike decltype of a member
         access expression).  */
      if (DECL_DECOMPOSITION_P (expr))
        {
          if (DECL_HAS_VALUE_EXPR_P (expr))
            /* Expr is an array or struct subobject proxy, handle
               bit-fields properly.  */
            return unlowered_expr_type (expr);
          else
            /* Expr is a reference variable for the tuple case.  */
            return lookup_decomp_type (expr);
        }
But for decltype(auto) c = a; we have do_auto_deduction called with init which is *a:
	  else if (AUTO_IS_DECLTYPE (auto_node))
	    {
	      bool id = (DECL_P (init)
			 || ((TREE_CODE (init) == COMPONENT_REF
			      || TREE_CODE (init) == SCOPE_REF)
			     && !REF_PARENTHESIZED_P (init)));
 thus id is false because init is not DECL_P and do_auto_deduction doesn't have special handling of DECL_DECOMPOSITION_P if id_expression_or_member_access_p
argument is false.
So, shall we for the above DECL_P (init) test test it with
  tree rinit = INDIRECT_REF_P (init) ? TREE_OPERAND (init, 0) : init;
or something similar (what about the COMPONENT_REF/SCOPE_REF case?  Shall we
pass the INDIRECT_REF to finish_decltype_type if INDIRECT_REF_P (init) or not?
Comment 2 Andrew Pinski 2021-08-03 05:14:26 UTC
Seems fixed in GCC 11+.
Comment 3 Marek Polacek 2021-12-03 19:02:08 UTC
Test added in abd7712f91c99690f8b0046ea168b2782afbac69.