diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c
index d776f2f9bca..e024c78cf51 100644
--- a/gdb/dwarf2/cooked-index.c
+++ b/gdb/dwarf2/cooked-index.c
@@ -93,39 +93,37 @@ int
 cooked_index_entry::compare (const char *stra, const char *strb,
 			     comparison_mode mode)
 {
-  auto munge = [] (char c) -> unsigned char
+  auto munge = [] (char c) constexpr -> unsigned char
     {
-      /* We want to sort '<' before any other printable character.
-	 So, rewrite '<' to something just before ' '.  */
+      /* Treat '<' as if it ended the string.  This lets something
+	 like "func<t>" match "func<t<int>>".  See the "Breakpoints in
+	 template functions" section in the manual.  */
       if (c == '<')
-	return '\x1f';
+	return '\0';
       return TOLOWER ((unsigned char) c);
     };
 
-  while (*stra != '\0'
-	 && *strb != '\0'
-	 && (munge (*stra) == munge (*strb)))
+  unsigned char a = munge (*stra);
+  unsigned char b = munge (*strb);
+
+  while (a != '\0' && b != '\0' && a == b)
     {
-      ++stra;
-      ++strb;
+      a = munge (*++stra);
+      b = munge (*++strb);
     }
 
-  unsigned char c1 = munge (*stra);
-  unsigned char c2 = munge (*strb);
-
-  if (c1 == c2)
+  if (a == b)
     return 0;
 
   /* When completing, if STRB ends earlier than STRA, consider them as
-     equal.  When comparing, if STRB ends earlier and STRA ends with
-     '<', consider them as equal.  */
-  if (mode == COMPLETE || (mode == MATCH && c1 == munge ('<')))
+     equal.  */
+  if (mode == COMPLETE || (mode == MATCH && a == munge ('<')))
     {
-      if (c2 == '\0')
+      if (b == '\0')
 	return 0;
     }
 
-  return c1 < c2 ? -1 : 1;
+  return a < b ? -1 : 1;
 }
 
 #if GDB_SELF_TEST
@@ -155,33 +153,36 @@ test_compare ()
 					   mode_complete) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("name", "name<>",
-					   mode_compare) < 0);
+					   mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<>", "name",
 					   mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name", "name<>",
-					   mode_complete) < 0);
+					   mode_complete) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<>", "name",
 					   mode_complete) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>",
 					   mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>",
-					   mode_compare) > 0);
+					   mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>",
 					   mode_complete) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>",
-					   mode_complete) > 0);
+					   mode_complete) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>",
 					   "name<arg<more>>",
 					   mode_compare) == 0);
+  SELF_CHECK (cooked_index_entry::compare ("name<arg>",
+					   "name<arg<more>>",
+					   mode_compare) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("name", "name<arg<more>>",
-					   mode_compare) < 0);
+					   mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name",
 					   mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<",
-					   mode_compare) > 0);
+					   mode_compare) == 0);
   SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<",
 					   mode_complete) == 0);
 
@@ -191,7 +192,7 @@ test_compare ()
   SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0);
 
   SELF_CHECK (cooked_index_entry::compare ("func", "func<type>",
-					   mode_sort) < 0);
+					   mode_sort) == 0);
   SELF_CHECK (cooked_index_entry::compare ("func<type>", "func1",
 					   mode_sort) < 0);
 }
diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.cc b/gdb/testsuite/gdb.linespec/cpcompletion.cc
new file mode 100644
index 00000000000..5b050d3814f
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/cpcompletion.cc
@@ -0,0 +1,23 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+extern int not_really_main ();
+
+int main ()
+{
+  return not_really_main ();
+}
diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp
index 09bd9a25d77..2293238d011 100644
--- a/gdb/testsuite/gdb.linespec/cpcompletion.exp
+++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp
@@ -18,14 +18,14 @@
 load_lib completion-support.exp
 load_lib data-structures.exp
 
-standard_testfile cpls.cc cpls2.cc cpls-hyphen.cc
+standard_testfile cpcompletion.cc cpls.cc cpls2.cc cpls-hyphen.cc
 
 set opts {}
 lappend opts debug
 lappend opts additional_flags=-std=c++11
 
 if {[prepare_for_testing "failed to prepare" $testfile \
-	 [list $srcfile $srcfile2 $srcfile3] $opts]} {
+	 [list $srcfile $srcfile2 $srcfile3 $srcfile4] $opts]} {
     return -1
 }
 
@@ -666,6 +666,7 @@ proc_with_prefix template-function-foo {} {
 	    $completion_list
 	check_setting_bp_fails "$cmd_prefix foo<A"
 
+	clean_restart $::testfile
 	# "foo<A>" should give any function with one parameter of any type
 	# of A.  While the parameter list in the template should be ignored,
 	# the function's argument list should not be ignored.
diff --git a/gdb/testsuite/gdb.linespec/cpls.cc b/gdb/testsuite/gdb.linespec/cpls.cc
index 9f24e47fb92..0ab7ad3e195 100644
--- a/gdb/testsuite/gdb.linespec/cpls.cc
+++ b/gdb/testsuite/gdb.linespec/cpls.cc
@@ -486,7 +486,7 @@ file_constrained_test_cpls_function (int i)
 
 
 int
-main ()
+not_really_main ()
 {
   template2_struct_inst.template2_fn<int, int> ();
   template_struct_int.template_overload_fn(0);
