diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index b845aff2226..d2358e2009a 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -998,6 +998,11 @@ c_common_post_options (const char **pfilename)
 		       warn_deprecated_enum_float_conv,
 		       cxx_dialect >= cxx20 && warn_deprecated);
 
+  /* -Wdeprecated-template-id-cdtor is enabled by default in C++20.  */
+  SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+		       warn_deprecated_template_id_cdtor,
+		       cxx_dialect >= cxx20 || warn_cxx20_compat);
+
   /* Declone C++ 'structors if -Os.  */
   if (flag_declone_ctor_dtor == -1)
     flag_declone_ctor_dtor = optimize_size;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9c0a28092fc..2e1d07b66de 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -642,6 +642,10 @@ C++ ObjC++ Var(warn_deprecated_enum_float_conv) Warning
 Warn about deprecated arithmetic conversions on operands where one is of enumeration
 type and the other is of a floating-point type.
 
+Wdeprecated-template-id-cdtor
+C++ ObjC++ Var(warn_deprecated_template_id_cdtor) Warning
+Warn about deprecated simple-template-id in a constructor or destructor.
+
 Wdesignated-init
 C ObjC Var(warn_designated_init) Init(1) Warning
 Warn about positional initialization of structs requiring designated initializers.
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 3748ccd49ff..473eaf4f1f7 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -6717,12 +6717,17 @@ cp_parser_unqualified_id (cp_parser* parser,
 
 	/* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
 	   declarator-id of a constructor or destructor.  */
-	if (token->type == CPP_TEMPLATE_ID && declarator_p
-	    && cxx_dialect >= cxx20)
+	if (token->type == CPP_TEMPLATE_ID
+	    && declarator_p
+	    && !cp_parser_uncommitted_to_tentative_parse_p (parser)
+	    && !cp_parser_simulate_error (parser))
 	  {
-	    if (!cp_parser_simulate_error (parser))
-	      error_at (tilde_loc, "template-id not allowed for destructor");
-	    return error_mark_node;
+	    auto_diagnostic_group d;
+	    if (emit_diagnostic (cxx_dialect >= cxx20 ? DK_PEDWARN : DK_WARNING,
+				 tilde_loc, OPT_Wdeprecated_template_id_cdtor,
+				 "template-id not allowed for destructor in "
+				 "C++20"))
+	      inform (tilde_loc, "remove the %qs", "< >");
 	  }
 
 	/* If there was an explicit qualification (S::~T), first look
@@ -32329,11 +32334,11 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
   if (next_token->type != CPP_NAME
       && next_token->type != CPP_SCOPE
       && next_token->type != CPP_NESTED_NAME_SPECIFIER
-      /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
-	 declarator-id of a constructor or destructor.  */
-      && (next_token->type != CPP_TEMPLATE_ID || cxx_dialect >= cxx20))
+      && next_token->type != CPP_TEMPLATE_ID)
     return false;
 
+  const bool saw_template_id = (next_token->type == CPP_TEMPLATE_ID);
+
   /* Parse tentatively; we are going to roll back all of the tokens
      consumed here.  */
   cp_parser_parse_tentatively (parser);
@@ -32550,6 +32555,19 @@ cp_parser_constructor_declarator_p (cp_parser *parser, cp_parser_flags flags,
   /* We did not really want to consume any tokens.  */
   cp_parser_abort_tentative_parse (parser);
 
+  /* DR 2237 (C++20 only): A simple-template-id is no longer valid as the
+     declarator-id of a constructor or destructor.  */
+  if (constructor_p
+      && saw_template_id
+      && !cp_parser_uncommitted_to_tentative_parse_p (parser))
+    {
+      auto_diagnostic_group d;
+      if (emit_diagnostic (cxx_dialect >= cxx20 ? DK_PEDWARN : DK_WARNING,
+			   input_location, OPT_Wdeprecated_template_id_cdtor,
+			   "template-id not allowed for constructor in C++20"))
+	inform (input_location, "remove the %qs", "< >");
+    }
+
   return constructor_p;
 }
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 71339b8b30f..5bd65bd02be 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -255,6 +255,7 @@ in the following sections.
 -Wdelete-non-virtual-dtor  -Wno-deprecated-array-compare
 -Wdeprecated-copy -Wdeprecated-copy-dtor
 -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion
+-Wno-deprecated-template-id-cdtor
 -Weffc++ -Wno-elaborated-enum-base
 -Wno-exceptions -Wextra-semi  -Wno-inaccessible-base
 -Wno-inherited-variadic-ctor  -Wno-init-list-lifetime
@@ -3989,6 +3990,23 @@ bool b = e <= 3.7;
 @option{-std=c++20}.  In pre-C++20 dialects, this warning can be enabled
 by @option{-Wenum-conversion}.
 
+@opindex Wdeprecated-template-id-cdtor
+@opindex Wno-deprecated-template-id-cdtor
+@item -Wno-deprecated-template-id-cdtor @r{(C++ and Objective-C++ only)}
+Disable the warning about the deprecated use of simple-template-id as the
+declarator-id of a constructor or destructor, which became invalid in C++20
+via DR 2237.  For example:
+
+@smallexample
+template<typename T> struct S @{
+  S<T>(); // should be S();
+  ~S<T>();  // should be ~S();
+@};
+@end smallexample
+
+@option{-Wdeprecated-template-id-cdtor} is enabled by default with
+@option{-std=c++20}; it is also enabled by @option{-Wc++20-compat}.
+
 @opindex Welaborated-enum-base
 @opindex Wno-elaborated-enum-base
 @item -Wno-elaborated-enum-base
diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-2.C b/gcc/testsuite/g++.dg/DRs/dr2237-2.C
new file mode 100644
index 00000000000..1d99347229c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2237-2.C
@@ -0,0 +1,9 @@
+// DR 2237 - Can a template-id name a constructor?
+// { dg-options "" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-warning "template-id not allowed for constructor" "" { target c++20 } }
+  X(int); // OK, injected-class-name used
+  ~X<T>(); // { dg-warning "template-id not allowed for destructor" "" { target c++20 } }
+};
diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-3.C b/gcc/testsuite/g++.dg/DRs/dr2237-3.C
new file mode 100644
index 00000000000..c8ad6852389
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2237-3.C
@@ -0,0 +1,16 @@
+// PR c++/107126
+// { dg-options "" }
+
+template<typename T>
+struct C
+{
+    ~C();
+};
+template<typename T>
+C<T>::~C<T>()	      // { dg-warning "template-id not allowed for destructor" "" { target c++20 } }
+{
+}
+int main()
+{
+    C<int> c;;
+}
diff --git a/gcc/testsuite/g++.dg/DRs/dr2237-4.C b/gcc/testsuite/g++.dg/DRs/dr2237-4.C
new file mode 100644
index 00000000000..a358dd521b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2237-4.C
@@ -0,0 +1,11 @@
+// PR c++/97202
+// { dg-options "" }
+
+template<typename T>
+struct F
+{
+  F<T>();  // { dg-warning "template-id not allowed for constructor" "" { target c++20 } }
+};
+
+template<typename T>
+inline F<T>::F() { }
diff --git a/gcc/testsuite/g++.dg/DRs/dr2237.C b/gcc/testsuite/g++.dg/DRs/dr2237.C
index f3d6d11e61e..830c8f5a2a6 100644
--- a/gcc/testsuite/g++.dg/DRs/dr2237.C
+++ b/gcc/testsuite/g++.dg/DRs/dr2237.C
@@ -2,7 +2,7 @@
 
 template<class T>
 struct X {
-  X<T>(); // { dg-error "expected" "" { target c++20 } }
+  X<T>(); // { dg-error "template-id not allowed for constructor" "" { target c++20 } }
   X(int); // OK, injected-class-name used
   ~X<T>(); // { dg-error "template-id not allowed for destructor" "" { target c++20 } }
 };
diff --git a/gcc/testsuite/g++.dg/parse/constructor2.C b/gcc/testsuite/g++.dg/parse/constructor2.C
index d620f41e027..e4b2ad3f2fe 100644
--- a/gcc/testsuite/g++.dg/parse/constructor2.C
+++ b/gcc/testsuite/g++.dg/parse/constructor2.C
@@ -1,11 +1,11 @@
 // PR c++/14260
 
-template <class TClass> 
-class T 
-{ 
-public: 
-  T(short,short f=0) {} 
-  T<TClass>(int f) {} // { dg-error "expected" "" { target c++20 } }
-  T<TClass>(int f=0,const char* b=0) {} // { dg-error "expected" "" { target c++20 } }
-}; 
+template <class TClass>
+class T
+{
+public:
+  T(short,short f=0) {}
+  T<TClass>(int f) {} // { dg-error "template-id not allowed for constructor" "" { target c++20 } }
+  T<TClass>(int f=0,const char* b=0) {} // { dg-error "template-id not allowed for constructor" "" { target c++20 } }
+};
 
diff --git a/gcc/testsuite/g++.dg/template/error34.C b/gcc/testsuite/g++.dg/template/error34.C
index ab688d9ba8c..921cb8fb729 100644
--- a/gcc/testsuite/g++.dg/template/error34.C
+++ b/gcc/testsuite/g++.dg/template/error34.C
@@ -3,27 +3,27 @@
 
 template<typename T> struct A
 {
-  A<__builtin_offsetof(T, x)>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\)|expected" }
+  A<__builtin_offsetof(T, x)>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\)|template-id" }
 };
 
 template<typename T> struct B
 {
-  B<__builtin_offsetof(T, x.y)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\)|expected" }
+  B<__builtin_offsetof(T, x.y)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\)|template-id" }
 };
 
 template<typename T> struct C
 {
-  C<__builtin_offsetof(T, x[6])>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\\[6\\\]\\)|expected" }
+  C<__builtin_offsetof(T, x[6])>();	// { dg-error "type/value mismatch|offsetof\\(T, x\\\[6\\\]\\)|template-id" }
 };
 
 template<typename T> struct D
 {
-  D<__builtin_offsetof(T, x.y[6].z)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\\[6\\\].z\\)|expected" }
+  D<__builtin_offsetof(T, x.y[6].z)>();	// { dg-error "type/value mismatch|offsetof\\(T, x.y\\\[6\\\].z\\)|template-id" }
 };
 
 struct E { int x; };
 
 template<typename T> struct F
 {
-  F<__builtin_offsetof(E, x)>();	// { dg-error "type/value mismatch|offsetof\\(E, x\\)|expected" }
+  F<__builtin_offsetof(E, x)>();	// { dg-error "type/value mismatch|offsetof\\(E, x\\)|template-id" }
 };
diff --git a/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-1.C b/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-1.C
new file mode 100644
index 00000000000..429490708bc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-1.C
@@ -0,0 +1,9 @@
+// PR c++/107126
+// { dg-do compile }
+// { dg-options "-Wc++20-compat" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-warning "template-id not allowed for constructor" }
+  ~X<T>(); // { dg-warning "template-id not allowed for destructor" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-2.C b/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-2.C
new file mode 100644
index 00000000000..d76b647c615
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-2.C
@@ -0,0 +1,9 @@
+// PR c++/107126
+// { dg-do compile }
+// { dg-options "-Wdeprecated-template-id-cdtor" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-warning "template-id not allowed for constructor" }
+  ~X<T>(); // { dg-warning "template-id not allowed for destructor" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-3.C b/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-3.C
new file mode 100644
index 00000000000..80fc2ff7739
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-3.C
@@ -0,0 +1,9 @@
+// PR c++/107126
+// { dg-do compile }
+// { dg-options "-Wc++20-compat -Wno-deprecated-template-id-cdtor" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-bogus "template-id not allowed for constructor" }
+  ~X<T>(); // { dg-bogus "template-id not allowed for destructor" }
+};
diff --git a/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-4.C b/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-4.C
new file mode 100644
index 00000000000..6f1747701da
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wdeprecated-template-id-cdtor-4.C
@@ -0,0 +1,9 @@
+// PR c++/107126
+// { dg-do compile }
+// { dg-options "-Wdeprecated-template-id-cdtor -Wno-c++20-compat" }
+
+template<class T>
+struct X {
+  X<T>(); // { dg-warning "template-id not allowed for constructor" }
+  ~X<T>(); // { dg-warning "template-id not allowed for destructor" }
+};
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C b/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C
index bf418ba48c7..56b4232d04b 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/ctor2.C
@@ -4,7 +4,7 @@
 
 template <class T>
 struct A {
-  A<T>(); // { dg-error "expected" "" { target c++20 } }
+  A<T>(); // { dg-error "template-id" "" { target c++20 } }
 };
 
 template <class T>
