diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 345090dae38..54917297b6a 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -5051,6 +5051,8 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned)
 	      if (t == NULL_TREE)
 		{
 		  t = make_node (code);
+		  if (flag_isoc23 && code != ENUMERAL_TYPE)
+		    SET_TYPE_STRUCTURAL_EQUALITY (t);
 		  pushtag (input_location, name, t);
 		}
 	    }
@@ -8809,6 +8811,8 @@ parser_xref_tag (location_t loc, enum tree_code code, tree name,
      the forward-reference will be altered into a real type.  */
 
   ref = make_node (code);
+  if (flag_isoc23 && code != ENUMERAL_TYPE)
+    SET_TYPE_STRUCTURAL_EQUALITY (ref);
   if (code == ENUMERAL_TYPE)
     {
       /* Give the type a default layout like unsigned int
@@ -8910,6 +8914,8 @@ start_struct (location_t loc, enum tree_code code, tree name,
   if (ref == NULL_TREE || TREE_CODE (ref) != code)
     {
       ref = make_node (code);
+      if (flag_isoc23)
+	SET_TYPE_STRUCTURAL_EQUALITY (ref);
       pushtag (loc, name, ref);
     }
 
@@ -9347,6 +9353,43 @@ is_flexible_array_member_p (bool is_last_field,
   return false;
 }
 
+/* Recompute TYPE_CANONICAL for qualified versions of the type and
+   related pointer types after an aggregate type has been finalized.
+   Will not update array types, pointers to array types, function
+   types and other derived types created while the type was still
+   incomplete, those will remain TYPE_STRUCTURAL_EQUALITY_P.  */
+
+static void
+c_update_type_canonical (tree t)
+{
+  for (tree x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
+    {
+      if (x != t
+	  && TYPE_STRUCTURAL_EQUALITY_P (x)
+	  && check_qualified_type (x, t, TYPE_QUALS (x)))
+	{
+	  if (TYPE_CANONICAL (t) != t)
+	    TYPE_CANONICAL (x)
+	      = build_qualified_type (TYPE_CANONICAL (t), TYPE_QUALS (x));
+	  else
+	    TYPE_CANONICAL (x) = x;
+	}
+      else if (x != t)
+	continue;
+      for (tree p = TYPE_POINTER_TO (x); p; p = TYPE_NEXT_PTR_TO (p))
+	{
+	  if (!TYPE_STRUCTURAL_EQUALITY_P (p))
+	    continue;
+	  if (TYPE_CANONICAL (x) != x || TYPE_REF_CAN_ALIAS_ALL (p))
+	    TYPE_CANONICAL (p)
+	      = build_pointer_type_for_mode (TYPE_CANONICAL (x), TYPE_MODE (p),
+					     false);
+	  else
+	    TYPE_CANONICAL (p) = p;
+	  c_update_type_canonical (p);
+	}
+    }
+}
 
 /* Fill in the fields of a RECORD_TYPE or UNION_TYPE node, T.
    LOC is the location of the RECORD_TYPE or UNION_TYPE's definition.
@@ -9695,11 +9738,12 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
   /* Set type canonical based on equivalence class.  */
   if (flag_isoc23)
     {
-      if (NULL == c_struct_htab)
+      if (c_struct_htab == NULL)
 	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
 
       hashval_t hash = c_struct_hasher::hash (t);
 
+      gcc_checking_assert (TYPE_STRUCTURAL_EQUALITY_P (t));
       tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
       if (*e)
 	TYPE_CANONICAL (t) = *e;
@@ -9708,6 +9752,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
 	  TYPE_CANONICAL (t) = t;
 	  *e = t;
 	}
+      c_update_type_canonical (t);
     }
 
   tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index ddeab1e2a8a..4567b114734 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -532,6 +532,7 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
 	  /* Otherwise, create a new type node and link it into the cache.  */
 
 	  tree n = make_node (code1);
+	  SET_TYPE_STRUCTURAL_EQUALITY (n);
 	  TYPE_NAME (n) = TYPE_NAME (t1);
 
 	  struct composite_cache cache2 = { t1, t2, n, cache };
@@ -590,7 +591,8 @@ composite_type_internal (tree t1, tree t2, struct composite_cache* cache)
 	  TYPE_STUB_DECL (n) = pushdecl (build_decl (input_location, TYPE_DECL,
 						     NULL_TREE, n));
 
-	  n = finish_struct(input_location, n, fields, attributes, NULL, &expr);
+	  n = finish_struct (input_location, n, fields, attributes, NULL,
+			     &expr);
 
 	  n = qualify_type (n, t1);
 
diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc
index 16ed55e2e5a..6f75444e513 100644
--- a/gcc/ipa-free-lang-data.cc
+++ b/gcc/ipa-free-lang-data.cc
@@ -255,7 +255,9 @@ fld_incomplete_type_of (tree t, class free_lang_data_d *fld)
 	    first = build_reference_type_for_mode (t2, TYPE_MODE (t),
 						   TYPE_REF_CAN_ALIAS_ALL (t));
 	  gcc_assert (TYPE_CANONICAL (t2) != t2
-		      && TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t)));
+		      && (TYPE_CANONICAL (t2) == TYPE_CANONICAL (TREE_TYPE (t))
+			  || TYPE_STRUCTURAL_EQUALITY_P (t2)
+			  || TYPE_STRUCTURAL_EQUALITY_P (TREE_TYPE (t))));
 	  if (!fld->pset.add (first))
 	    add_tree_to_fld_list (first, fld);
 	  return fld_type_variant (first, t, fld);
diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
new file mode 100644
index 00000000000..82d652569e9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23 -g" } */
+
+struct a;
+typedef struct a b;
+
+void g() {
+    struct a { b* x; };
+}
+
+struct a { b* x; };
+
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
new file mode 100644
index 00000000000..bc47a04ece5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-incomplete-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile }
+ * { dg-options "-std=c23 -g" } */
+
+struct a;
+typedef struct a b;
+
+void f() {
+	extern struct a { b* x; } t;
+}
+
+extern struct a { b* x; } t;
+
+
diff --git a/gcc/testsuite/gcc.dg/pr114361.c b/gcc/testsuite/gcc.dg/pr114361.c
new file mode 100644
index 00000000000..0f3feb53566
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr114361.c
@@ -0,0 +1,11 @@
+/* PR c/114361 */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu23 -g" } */
+
+void f()
+{
+    typedef struct foo bar;
+    typedef __typeof( ({ (struct foo { bar *x; }){ }; }) ) wuz;
+    struct foo { wuz *x; };
+}
+
diff --git a/gcc/testsuite/gcc.dg/pr114574-1.c b/gcc/testsuite/gcc.dg/pr114574-1.c
new file mode 100644
index 00000000000..060dcdbe73e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr114574-1.c
@@ -0,0 +1,10 @@
+/* PR lto/114574
+ * { dg-do compile }
+ * { dg-options "-flto" } */
+
+const struct S * x;
+struct S {};
+void f(const struct S **);
+
+
+
diff --git a/gcc/testsuite/gcc.dg/pr114574-2.c b/gcc/testsuite/gcc.dg/pr114574-2.c
new file mode 100644
index 00000000000..723291e2211
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr114574-2.c
@@ -0,0 +1,10 @@
+/* PR lto/114574
+ * { dg-do compile }
+ * { dg-options "-flto -std=c23" } */
+
+const struct S * x;
+struct S {};
+void f(const struct S **);
+
+
+
