diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 1acc9cd5cb9..bcb33c18aa4 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1165,11 +1165,11 @@ ftms = {
   // 202305 P2757R3 Type checking format args
   // 202306 P2637R3 Member visit
   // 202311 P2918R2 Runtime format strings II
-  // values = {
-    // v = 202305;
-    // cxxmin = 26;
-    // hosted = yes;
-  // };
+  values = {
+    v = 202305;
+    cxxmin = 26;
+    hosted = yes;
+  };
   // 201907 Text Formatting, Integration of chrono, printf corner cases.
   // 202106 std::format improvements.
   // 202110 Fixing locale handling in chrono formatters, generator-like types.
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 5cd77770e21..4d1af34bf8d 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1304,7 +1304,12 @@
 #undef __glibcxx_want_barrier
 
 #if !defined(__cpp_lib_format)
-# if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED
+# if (__cplusplus >  202302L) && _GLIBCXX_HOSTED
+#  define __glibcxx_format 202305L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_format)
+#   define __cpp_lib_format 202305L
+#  endif
+# elif (__cplusplus >= 202002L) && _GLIBCXX_HOSTED
 #  define __glibcxx_format 202304L
 #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_format)
 #   define __cpp_lib_format 202304L
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 487177b5294..0e61bc5e6bc 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -222,6 +222,9 @@ namespace __format
   inline void
   __failed_to_parse_format_spec()
   { __throw_format_error("format error: failed to parse format-spec"); }
+
+  template<typename _CharT> class _Scanner;
+
 } // namespace __format
   /// @endcond
 
@@ -241,9 +244,8 @@ namespace __format
       using iterator = const_iterator;
 
       constexpr explicit
-      basic_format_parse_context(basic_string_view<_CharT> __fmt,
-				 size_t __num_args = 0) noexcept
-      : _M_begin(__fmt.begin()), _M_end(__fmt.end()), _M_num_args(__num_args)
+      basic_format_parse_context(basic_string_view<_CharT> __fmt) noexcept
+      : _M_begin(__fmt.begin()), _M_end(__fmt.end())
       { }
 
       basic_format_parse_context(const basic_format_parse_context&) = delete;
@@ -283,13 +285,78 @@ namespace __format
 	    __format::__invalid_arg_id_in_format_string();
       }
 
+#if __cpp_lib_format >= 202305L
+      template<typename... _Ts>
+	constexpr void
+	check_dynamic_spec(size_t __id) noexcept;
+
+      constexpr void
+      check_dynamic_spec_integral(size_t __id) noexcept
+      {
+	check_dynamic_spec<int, unsigned, long long, unsigned long long>(__id);
+      }
+
+      constexpr void
+      check_dynamic_spec_string(size_t __id) noexcept
+      {
+	check_dynamic_spec<const char_type*, basic_string_view<_CharT>>(__id);
+      }
+
+    private:
+      // Check the Mandates: condition for check_dynamic_spec<Ts...>(n)
+      template<typename... _Ts>
+	static consteval bool
+	__check_dynamic_spec_types()
+	{
+	  if constexpr (sizeof...(_Ts))
+	    {
+	      int __counts[] = {
+		(is_same_v<bool, _Ts> + ...),
+		(is_same_v<_CharT, _Ts> + ...),
+		(is_same_v<int, _Ts> + ...),
+		(is_same_v<unsigned, _Ts> + ...),
+		(is_same_v<long long, _Ts> + ...),
+		(is_same_v<unsigned long long, _Ts> + ...),
+		(is_same_v<float, _Ts> + ...),
+		(is_same_v<double, _Ts> + ...),
+		(is_same_v<long double, _Ts> + ...),
+		(is_same_v<const _CharT*, _Ts> + ...),
+		(is_same_v<basic_string_view<_CharT>, _Ts> + ...),
+		(is_same_v<const void*, _Ts> + ...)
+	      };
+	      int __sum = 0;
+	      for (int __c : __counts)
+		{
+		  __sum += __c;
+		  if (__c > 1)
+		    __invalid_dynamic_spec("non-unique template argument type");
+		}
+	      if (__sum != sizeof...(_Ts))
+		__invalid_dynamic_spec("disallowed template argument type");
+	    }
+	  return true;
+	}
+
+      // This must not be constexpr.
+      static void __invalid_dynamic_spec(const char*);
+
+      friend __format::_Scanner<_CharT>;
+#endif
+
+      // This constructor should only be used by the implementation.
+      constexpr explicit
+      basic_format_parse_context(basic_string_view<_CharT> __fmt,
+				 size_t __num_args) noexcept
+      : _M_begin(__fmt.begin()), _M_end(__fmt.end()), _M_num_args(__num_args)
+      { }
+
     private:
       iterator _M_begin;
       iterator _M_end;
       enum _Indexing { _Unknown, _Manual, _Auto };
       _Indexing _M_indexing = _Unknown;
       size_t _M_next_arg_id = 0;
-      size_t _M_num_args;
+      size_t _M_num_args = 0;
     };
 
 /// @cond undocumented
@@ -549,6 +616,9 @@ namespace __format
 		__pc.check_arg_id(__v);
 		__val = __v;
 	      }
+#if __cpp_lib_format >= 202305L
+	    __pc.check_dynamic_spec_integral(__val);
+#endif
 	    ++__first; // past the '}'
 	  }
 	return __first;
@@ -3205,6 +3275,10 @@ namespace __format
   template<typename _Context, typename... _Args>
     class _Arg_store;
 
+  template<typename _Ch, typename _Tp>
+    consteval _Arg_t
+    __to_arg_t_enum() noexcept;
+
 } // namespace __format
 /// @endcond
 
@@ -3486,6 +3560,10 @@ namespace __format
 	friend decltype(auto)
 	visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>);
 
+      template<typename _Ch, typename _Tp>
+	friend consteval __format::_Arg_t
+	__format::__to_arg_t_enum() noexcept;
+
       template<typename _Visitor>
 	decltype(auto)
 	_M_visit(_Visitor&& __vis, __format::_Arg_t __type)
@@ -3901,7 +3979,11 @@ namespace __format
     {
       using iterator = typename basic_format_parse_context<_CharT>::iterator;
 
-      basic_format_parse_context<_CharT> _M_pc;
+      struct _Parse_context : basic_format_parse_context<_CharT>
+      {
+	using basic_format_parse_context<_CharT>::basic_format_parse_context;
+	const _Arg_t* _M_types = nullptr;
+      } _M_pc;
 
       constexpr explicit
       _Scanner(basic_string_view<_CharT> __str, size_t __nargs = -1)
@@ -4061,6 +4143,16 @@ namespace __format
       }
     };
 
+  template<typename _CharT, typename _Tp>
+    consteval _Arg_t
+    __to_arg_t_enum() noexcept
+    {
+      using _Context = __format::__format_context<_CharT>;
+      using _Fmt_arg = basic_format_arg<_Context>;
+      using _NormalizedTp = typename _Fmt_arg::template _Normalize<_Tp>;
+      return _Fmt_arg::template _S_to_enum<_NormalizedTp>();
+    }
+
   // Validate a format string for Args.
   template<typename _CharT, typename... _Args>
     class _Checking_scanner : public _Scanner<_CharT>
@@ -4070,10 +4162,14 @@ namespace __format
 	"std::formatter must be specialized for each type being formatted");
 
     public:
-      constexpr
+      consteval
       _Checking_scanner(basic_string_view<_CharT> __str)
       : _Scanner<_CharT>(__str, sizeof...(_Args))
-      { }
+      {
+#if __cpp_lib_format >= 202305L
+	this->_M_pc._M_types = _M_types.data();
+#endif
+      }
 
     private:
       constexpr void
@@ -4104,6 +4200,11 @@ namespace __format
 	  else
 	    __builtin_unreachable();
 	}
+
+#if __cpp_lib_format >= 202305L
+      array<_Arg_t, sizeof...(_Args)>
+	_M_types{ { __format::__to_arg_t_enum<_CharT, _Args>()... } };
+#endif
     };
 
   template<typename _Out, typename _CharT, typename _Context>
@@ -4202,6 +4303,33 @@ namespace __format
 } // namespace __format
 /// @endcond
 
+#if __cpp_lib_format >= 202305L
+  template<typename _CharT>
+  template<typename... _Ts>
+    constexpr void
+    basic_format_parse_context<_CharT>::check_dynamic_spec(size_t __id) noexcept
+    {
+      constexpr bool __ok = __check_dynamic_spec_types<_Ts...>();
+
+      if consteval {
+	if (__id >= _M_num_args)
+	  __format::__invalid_arg_id_in_format_string();
+	if constexpr (sizeof...(_Ts) != 0)
+	  {
+	    using _Parse_context = __format::_Scanner<_CharT>::_Parse_context;
+	    auto __arg = static_cast<_Parse_context*>(this)->_M_types[__id];
+	    __format::_Arg_t __types[] = {
+	      __format::__to_arg_t_enum<_CharT, _Ts>()...
+	    };
+	    for (auto __t : __types)
+	      if (__arg == __t)
+		return;
+	  }
+	__invalid_dynamic_spec("arg(id) type does not match");
+      }
+    }
+#endif
+
   template<typename _CharT, typename... _Args>
     template<typename _Tp>
       requires convertible_to<const _Tp&, basic_string_view<_CharT>>
diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc
index 5152bb0b0d0..3c441d711b3 100644
--- a/libstdc++-v3/testsuite/std/format/functions/format.cc
+++ b/libstdc++-v3/testsuite/std/format/functions/format.cc
@@ -8,6 +8,8 @@
 # error "Feature test macro for std::format is missing in <format>"
 #elif __cpp_lib_format < 202110L
 # error "Feature test macro for std::format has wrong value in <format>"
+#elif __cplusplus > 202302L && __cpp_lib_format < 202305L
+# error "Feature test macro for std::format has wrong value in <format>"
 #endif
 
 #ifndef __cpp_lib_format_uchar
@@ -22,6 +24,8 @@
 # error "Feature test macro for std::format is missing in <version>"
 #elif __cpp_lib_format < 202110L
 # error "Feature test macro for std::format has wrong value in <version>"
+#elif __cplusplus > 202302L && __cpp_lib_format < 202305L
+# error "Feature test macro for std::format has wrong value in <version>"
 #endif
 
 #ifndef __cpp_lib_format_uchar
diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc b/libstdc++-v3/testsuite/std/format/parse_ctx.cc
index 3b3201c2a47..b0cef2da41b 100644
--- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc
+++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc
@@ -3,6 +3,91 @@
 #include <format>
 #include <testsuite_hooks.h>
 
+static_assert(std::is_constructible_v<std::format_parse_context,
+				      std::string_view>);
+static_assert(std::is_constructible_v<std::wformat_parse_context,
+				      std::wstring_view>);
+
+#if __cpp_lib_format < 202305
+constexpr bool construct_with_num_args = true;
+#else
+constexpr bool construct_with_num_args = false;
+#endif
+
+static_assert(std::is_constructible_v<std::format_parse_context,
+				      std::string_view, std::size_t>
+				      == construct_with_num_args);
+static_assert(std::is_constructible_v<std::wformat_parse_context,
+				      std::wstring_view, std::size_t>
+				      == construct_with_num_args);
+
+static_assert( ! std::is_constructible_v<std::format_parse_context,
+					 std::wstring_view>);
+static_assert( ! std::is_constructible_v<std::wformat_parse_context,
+					 std::string_view>);
+
+static_assert( ! std::is_convertible_v<std::string_view,
+				       std::format_parse_context> );
+static_assert( ! std::is_convertible_v<std::wstring_view,
+				       std::wformat_parse_context> );
+
+static_assert( ! std::is_default_constructible_v<std::format_parse_context> );
+static_assert( ! std::is_copy_constructible_v<std::format_parse_context> );
+static_assert( ! std::is_move_constructible_v<std::format_parse_context> );
+static_assert( ! std::is_copy_assignable_v<std::format_parse_context> );
+static_assert( ! std::is_move_assignable_v<std::format_parse_context> );
+
+// This concept is satisfied if the next_arg_id() call is a constant expression
+template<typename Ch, typename PC = std::basic_format_parse_context<Ch>>
+concept arg_id_available = requires {
+  typename std::integral_constant<std::size_t,
+				  PC({}).next_arg_id()>::type;
+};
+
+void
+test_members()
+{
+  std::string_view s = "spec string";
+
+  std::format_parse_context pc(s);
+
+  VERIFY( pc.begin() == s.begin() );
+  VERIFY( pc.end() == s.end() );
+  pc.advance_to(s.begin() + 5);
+  VERIFY( pc.begin() == s.begin() + 5 );
+
+  // Runtime calls to these do not check for the correct number of args.
+  VERIFY( pc.next_arg_id() == 0 );
+  VERIFY( pc.next_arg_id() == 1 );
+  VERIFY( pc.next_arg_id() == 2 );
+  try
+  {
+    // Cannot mix manual and automatic indexing.
+    pc.check_arg_id(0);
+    VERIFY( false );
+  }
+  catch (const std::format_error&)
+  {
+  }
+  // But they do check during constant evaluation:
+  VERIFY( ! arg_id_available<char> );
+  VERIFY( ! arg_id_available<wchar_t> );
+
+  std::format_parse_context pc2("");
+  pc2.check_arg_id(2);
+  pc2.check_arg_id(1);
+  pc2.check_arg_id(3);
+  try
+  {
+    // Cannot mix manual and automatic indexing.
+    (void) pc2.next_arg_id();
+    VERIFY( false );
+  }
+  catch (const std::format_error&)
+  {
+  }
+}
+
 template<typename T, bool auto_indexing = true>
 bool
 is_std_format_spec_for(std::string_view spec)
@@ -357,6 +442,65 @@ test_custom()
   VERIFY( ! is_std_format_spec_for<S>("") );
 }
 
+#if __cpp_lib_format >= 202305
+struct X { };
+
+template<>
+struct std::formatter<X, char>
+{
+  constexpr std::format_parse_context::iterator
+  parse(std::format_parse_context& pc)
+  {
+    std::string_view spec(pc.begin(), pc.end());
+    auto p = spec.find('}');
+    if (p != std::string_view::npos)
+      spec = spec.substr(0, p); // truncate to closing brace
+    if (spec == "int")
+    {
+      pc.check_dynamic_spec_integral(pc.next_arg_id());
+      integer = true;
+    }
+    else if (spec == "str")
+    {
+      pc.check_dynamic_spec_string(pc.next_arg_id());
+      integer = false;
+    }
+    else
+      throw std::format_error("invalid format-spec");
+    return pc.begin() + spec.size();
+  }
+
+  std::format_context::iterator
+  format(X, std::format_context& c) const
+  {
+    std::visit_format_arg([this]<typename T>(T) {
+      if (is_integral_v<T> != this->integer)
+	throw std::format_error("invalid argument type");
+    }, c.arg(1));
+    return c.out();
+  }
+private:
+  bool integer = false;
+};
+#endif
+
+void
+test_dynamic_type_check()
+{
+#if __cpp_lib_format >= 202305
+  std::format_parse_context pc("{1}.{2}");
+
+  // None of these calls should do anything at runtime, only during consteval:
+  pc.check_dynamic_spec<>(0);
+  pc.check_dynamic_spec<int, const char*>(0);
+  pc.check_dynamic_spec_integral(0);
+  pc.check_dynamic_spec_string(0);
+
+  (void) std::format("{:int}", X{}, 42L);
+  (void) std::format("{:str}", X{}, "H2G2");
+#endif
+}
+
 int main()
 {
   test_char();
@@ -366,4 +510,5 @@ int main()
   test_string();
   test_pointer();
   test_custom();
+  test_dynamic_type_check();
 }
diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx_neg.cc b/libstdc++-v3/testsuite/std/format/parse_ctx_neg.cc
new file mode 100644
index 00000000000..d6a4366d7d0
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/format/parse_ctx_neg.cc
@@ -0,0 +1,39 @@
+// { dg-do compile { target c++26 } }
+
+#include <format>
+
+void
+test_invalid()
+{
+  std::format_parse_context pc("");
+
+  // These types are all valid:
+  pc.check_dynamic_spec<bool, char, int, unsigned, long long,
+			unsigned long long, float, double, long double,
+			const char*, std::string_view, const void*>(0);
+  // For some reason, an empty pack of types is valid:
+  pc.check_dynamic_spec<>(0);
+
+  pc.check_dynamic_spec<void>(0); // { dg-error "here" }
+  // const void* is allowed, but void* is not
+  pc.check_dynamic_spec<void*>(0); // { dg-error "here" }
+  // int and long long are allowed, but long is not
+  pc.check_dynamic_spec<long>(0); // { dg-error "here" }
+  // char_type is allowed, but other character types are not
+  pc.check_dynamic_spec<wchar_t>(0); // { dg-error "here" }
+  pc.check_dynamic_spec<char8_t>(0); // { dg-error "here" }
+  // std::string_view is allowed, but std::string is not
+  pc.check_dynamic_spec<std::string>(0); // { dg-error "here" }
+  pc.check_dynamic_spec<int, bool, int>(0); // { dg-error "here" }
+
+  std::wformat_parse_context wpc(L"");
+  wpc.check_dynamic_spec<bool, wchar_t, int, unsigned, long long,
+			 unsigned long long, float, double, long double,
+			 const wchar_t*, std::wstring_view, const void*>(0);
+  wpc.check_dynamic_spec<char>(0); // { dg-error "here" }
+  wpc.check_dynamic_spec<char16_t>(0); // { dg-error "here" }
+  wpc.check_dynamic_spec<char32_t>(0); // { dg-error "here" }
+}
+
+// Each failure above will point to a call to this non-constexpr function:
+// { dg-error "__invalid_dynamic_spec" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc
index ddb3c5625cd..d5f30500139 100644
--- a/libstdc++-v3/testsuite/std/format/string.cc
+++ b/libstdc++-v3/testsuite/std/format/string.cc
@@ -109,9 +109,16 @@ test_format_spec()
   VERIFY( ! is_format_string_for("{:#?}", "str") );
   VERIFY( ! is_format_string_for("{:#?}", 'c') );
 
+  // The 0 option is not valid for charT and bool.
   VERIFY( ! is_format_string_for("{:0c}", 'c') );
   VERIFY( ! is_format_string_for("{:0s}", true) );
 
+  // Dynamic width arg must be an integer type.
+  VERIFY( ! is_format_string_for("{:{}d}", 1, 1.5) );
+  VERIFY( ! is_format_string_for("{:{}d}", 1, true) );
+  VERIFY( ! is_format_string_for("{:{}d}", 1, "str") );
+  VERIFY( ! is_format_string_for("{:{}d}", 1, nullptr) );
+
   // Precision only valid for string and floating-point types.
   VERIFY( ! is_format_string_for("{:.3d}", 1) );
   VERIFY( ! is_format_string_for("{:3.3d}", 1) );
@@ -119,6 +126,12 @@ test_format_spec()
   VERIFY( ! is_format_string_for("{:3.3s}", 'c') );
   VERIFY( ! is_format_string_for("{:3.3p}", nullptr) );
 
+  // Dynamic precision arg must be an integer type.
+  VERIFY( ! is_format_string_for("{:.{}f}", 1.0, 1.5) );
+  VERIFY( ! is_format_string_for("{:.{}f}", 1.0, true) );
+  VERIFY( ! is_format_string_for("{:.{}f}", 1.0, "str") );
+  VERIFY( ! is_format_string_for("{:.{}f}", 1.0, nullptr) );
+
   // Invalid presentation types for integers.
   VERIFY( ! is_format_string_for("{:f}", 1) );
   VERIFY( ! is_format_string_for("{:s}", 1) );
