[committed] libstdc++: Improve diagnostics for std::get with invalid tuple index

Message ID YPIDXlo7kRU/Q0lN@redhat.com
State New
Headers show
Series
  • [committed] libstdc++: Improve diagnostics for std::get with invalid tuple index
Related show

Commit Message

Michael Meissner via Gcc-patches July 16, 2021, 10:08 p.m.
The recent fix for std::get<T> uses a deleted overload to give better
diagnostics for out-of-range indices. This does something similar for
std::get<I>.

Tested powerpc64le-linux. Committed to trunk.


This adds a deleted overload of std::get<I>(const tuple<Types...>&).
Invalid calls with an out of range index will match the deleted overload
and give a single, clear error about calling a deleted function, instead
of overload resolution errors for every std::get overload in the
library.

This changes the current output of 15+ errors (plus notes and associated
header context) into just two errors (plus context):

error: static assertion failed: tuple index must be in range
error: use of deleted function 'constexpr std::__enable_if_t<(__i >= sizeof... (_Types))> std::get(const std::tuple<_Types ...>&) [with long unsigned int __i = 1; _Elements = {int}; std::__enable_if_t<(__i >= sizeof... (_Types))> = void]'

This seems like a nice improvement, although PR c++/66968 means that
"_Types" is printed in the signature rather than "_Elements".

Signed-off-by: Jonathan Wakely <jwakely@redhat.com>


libstdc++-v3/ChangeLog:

         * include/std/tuple (get<I>): Add deleted overload for bad
         index.
         * testsuite/20_util/tuple/element_access/get_neg.cc: Adjust
         expected errors.

Patch

commit 3dbc7b809a62167b36f217ab5f43207be19e5908
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Jul 16 20:59:43 2021

    libstdc++: Improve diagnostics for std::get with invalid tuple index
    
    This adds a deleted overload of std::get<I>(const tuple<Types...>&).
    Invalid calls with an out of range index will match the deleted overload
    and give a single, clear error about calling a deleted function, instead
    of overload resolution errors for every std::get overload in the
    library.
    
    This changes the current output of 15+ errors (plus notes and associated
    header context) into just two errors (plus context):
    
    error: static assertion failed: tuple index must be in range
    error: use of deleted function 'constexpr std::__enable_if_t<(__i >= sizeof... (_Types))> std::get(const std::tuple<_Types ...>&) [with long unsigned int __i = 1; _Elements = {int}; std::__enable_if_t<(__i >= sizeof... (_Types))> = void]'
    
    This seems like a nice improvement, although PR c++/66968 means that
    "_Types" is printed in the signature rather than "_Elements".
    
    Signed-off-by: Jonathan Wakely <jwakely@redhat.com>
    
    libstdc++-v3/ChangeLog:
    
            * include/std/tuple (get<I>): Add deleted overload for bad
            index.
            * testsuite/20_util/tuple/element_access/get_neg.cc: Adjust
            expected errors.

diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 6953f8715d7..8ee0d2f1ef5 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -1406,6 +1406,13 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return std::forward<const __element_type>(std::__get_helper<__i>(__t));
     }
 
+  /// @cond undocumented
+  // Deleted overload chosen for invalid indices.
+  template<size_t __i, typename... _Elements>
+    constexpr __enable_if_t<(__i >= sizeof...(_Elements))>
+    get(const tuple<_Elements...>&) = delete;
+  /// @endcond
+
 #if __cplusplus >= 201402L
 
 #define __cpp_lib_tuples_by_type 201304
diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
index cd850fdc21b..225bb6245a6 100644
--- a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc
@@ -25,12 +25,12 @@  test01()
 {
   using test_type = std::tuple<>;
   test_type t;
-  std::get<0>(t);				// { dg-error "no match" }
-  std::get<0>(const_cast<const test_type&>(t));	// { dg-error "no match" }
-  std::get<0>(static_cast<test_type&&>(t));	// { dg-error "no match" }
-  std::get<5>(t);				// { dg-error "no match" }
-  std::get<5>(const_cast<const test_type&>(t));	// { dg-error "no match" }
-  std::get<5>(static_cast<test_type&&>(t));	// { dg-error "no match" }
+  std::get<0>(t);				// { dg-error "deleted" }
+  std::get<0>(const_cast<const test_type&>(t));	// { dg-error "deleted" }
+  std::get<0>(static_cast<test_type&&>(t));	// { dg-error "deleted" }
+  std::get<5>(t);				// { dg-error "deleted" }
+  std::get<5>(const_cast<const test_type&>(t));	// { dg-error "deleted" }
+  std::get<5>(static_cast<test_type&&>(t));	// { dg-error "deleted" }
 }
 
 void
@@ -38,12 +38,12 @@  test02()
 {
   using test_type = std::tuple<int>;
   test_type t;
-  std::get<1>(t);				// { dg-error "no match" }
-  std::get<1>(const_cast<const test_type&>(t));	// { dg-error "no match" }
-  std::get<1>(static_cast<test_type&&>(t));	// { dg-error "no match" }
-  std::get<5>(t);				// { dg-error "no match" }
-  std::get<5>(const_cast<const test_type&>(t));	// { dg-error "no match" }
-  std::get<5>(static_cast<test_type&&>(t));	// { dg-error "no match" }
+  std::get<1>(t);				// { dg-error "deleted" }
+  std::get<1>(const_cast<const test_type&>(t));	// { dg-error "deleted" }
+  std::get<1>(static_cast<test_type&&>(t));	// { dg-error "deleted" }
+  std::get<5>(t);				// { dg-error "deleted" }
+  std::get<5>(const_cast<const test_type&>(t));	// { dg-error "deleted" }
+  std::get<5>(static_cast<test_type&&>(t));	// { dg-error "deleted" }
 }
 
 void
@@ -51,15 +51,12 @@  test03()
 {
   using test_type = std::tuple<int, int, int, int>;
   test_type t;
-  std::get<5>(t);				// { dg-error "no match" }
-  std::get<5>(const_cast<const test_type&>(t));	// { dg-error "no match" }
-  std::get<5>(static_cast<test_type&&>(t));	// { dg-error "no match" }
-  std::get<6>(t);				// { dg-error "no match" }
-  std::get<6>(const_cast<const test_type&>(t));	// { dg-error "no match" }
-  std::get<6>(static_cast<test_type&&>(t));	// { dg-error "no match" }
+  std::get<5>(t);				// { dg-error "deleted" }
+  std::get<5>(const_cast<const test_type&>(t));	// { dg-error "deleted" }
+  std::get<5>(static_cast<test_type&&>(t));	// { dg-error "deleted" }
+  std::get<6>(t);				// { dg-error "deleted" }
+  std::get<6>(const_cast<const test_type&>(t));	// { dg-error "deleted" }
+  std::get<6>(static_cast<test_type&&>(t));	// { dg-error "deleted" }
 }
 
-// { dg-prune-output "tuple index must be in range" }
-// { dg-prune-output "no type named .type" }
-// { dg-prune-output "type/value mismatch" }
-// { dg-prune-output "use of deleted function" }
+// { dg-error "tuple index must be in range" "" { target *-*-* } 0 }