+2021-01-05  Nick Alcock  <nick.alcock@oracle.com>
+
+       * ctf-api.h (ECTF_INCOMPLETE): New.
+       (ECTF_NERR): Adjust.
+
 2021-01-01  Nicolas Boulenguez  <nicolas@debian.org>
 
        * coff/internal.h: Correct comment spelling.
 
   _CTF_ITEM (ECTF_NEXT_WRONGFUN, "Wrong iteration function called.") \
   _CTF_ITEM (ECTF_NEXT_WRONGFP, "Iteration entity changed in mid-iterate.") \
   _CTF_ITEM (ECTF_FLAGS, "CTF header contains flags unknown to libctf.") \
-  _CTF_ITEM (ECTF_NEEDSBFD, "This feature needs a libctf with BFD support.")
+  _CTF_ITEM (ECTF_NEEDSBFD, "This feature needs a libctf with BFD support.") \
+  _CTF_ITEM (ECTF_INCOMPLETE, "Type is not a complete type.")
 
 #define        ECTF_BASE       1000    /* Base value for libctf errnos.  */
 
 #undef _CTF_FIRST
   };
 
-#define ECTF_NERR (ECTF_NEEDSBFD - ECTF_BASE + 1) /* Count of CTF errors.  */
+#define ECTF_NERR (ECTF_INCOMPLETE - ECTF_BASE + 1) /* Count of CTF errors.  */
 
 /* The CTF data model is inferred to be the caller's data model or the data
    model of the given object, unless ctf_setmodel is explicitly called.  */
 
+2021-01-05  Nick Alcock  <nick.alcock@oracle.com>
+
+       * testsuite/ld-ctf/conflicting-cycle-1.parent.d: Adjust for dumper
+       changes.
+       * testsuite/ld-ctf/cross-tu-cyclic-conflicting.d: Likewise.
+       * testsuite/ld-ctf/forward.c: New test...
+       * testsuite/ld-ctf/forward.d: ... and results.
+
 2021-01-05  Nick Alcock  <nick.alcock@oracle.com>
 
        * testsuite/ld-ctf/array.d: Adjust for dumper changes.
 
 #...
   Types:
 #...
-     0x[0-9a-f]*: struct B \(.*
-           *\[0x0\] \(ID 0x[0-9a-f]*\) \(kind 9\) struct B \(.*
+     0x[0-9a-f]*: struct B
+           *\[0x0\] \(ID 0x[0-9a-f]*\) \(kind 9\) struct B
 #...
 CTF archive member: .*:
 #...
 
      0x[0-9a-f]*: int \[0x0:0x[0-9a-f]*\] \(size 0x[0-9a-f]*\)
            *\[0x0\] \(ID 0x[0-9a-f]*\) \(kind 1\) int:[0-9]* \(aligned at 0x[0-9a-f]*, format 0x1, offset:bits 0x0:0x[0-9a-f]*\)
 #...
-     0x[0-9a-f]*: struct A .*
-           *\[0x0\] \(ID 0x[0-9a-f]*\) \(kind 9\) struct A .*
+     0x[0-9a-f]*: struct A
+           *\[0x0\] \(ID 0x[0-9a-f]*\) \(kind 9\) struct A
 #...
      0x[0-9a-f]*: struct C .*
            *\[0x0\] \(ID 0x[0-9a-f]*\) \(kind 6\) struct C .*
 
--- /dev/null
+struct foo;
+struct foo *bar __attribute__((used));
 
--- /dev/null
+#as:
+#source: forward.c
+#objdump: --ctf=.ctf
+#ld: -shared
+#name: Forwards
+
+.*: +file format .*
+
+Contents of CTF section .ctf:
+
+  Header:
+    Magic number: 0xdff2
+    Version: 4 \(CTF_VERSION_3\)
+#...
+    Type section:      .* \(0x18 bytes\)
+#...
+  Types:
+
+     0x[0-9a-f]: struct foo
+          *\[0x0\] \(ID 0x[0-9a-f]*\) \(kind 9\) struct foo
+     0x[0-9a-f]: struct foo \* \(size 0x[0-9a-f]*\) -> 0x[0-9a-f]: struct foo
+          *\[0x0\] \(ID 0x[0-9a-f]\) \(kind 3\) struct foo \* \(aligned at 0x[0-9a-f]*\)
+#...
 
+2021-01-05  Nick Alcock  <nick.alcock@oracle.com>
+
+       * ctf-types.c (ctf_type_resolve): Improve comment.
+       (ctf_type_size): Yield ECTF_INCOMPLETE when applied to forwards.
+       Emit errors into the right dict.
+       (ctf_type_align): Likewise.
+       * ctf-create.c (ctf_add_member_offset): Yield ECTF_INCOMPLETE
+       when adding a member without explicit offset when this member, or
+       the previous member, is incomplete.
+       * ctf-dump.c (ctf_dump_format_type): Do not try to print the size of
+       forwards.
+       (ctf_dump_member): Do not try to print their alignment.
+
 2021-01-05  Nick Alcock  <nick.alcock@oracle.com>
 
        * ctf-dump.c (ctf_dump_objts): Dump by calling ctf_dump_format_type.
 
   if (ctf_lookup_by_id (&tmp, arp->ctr_index) == NULL)
     return CTF_ERR;            /* errno is set for us.  */
 
+  if (ctf_type_kind (fp, arp->ctr_index) == CTF_K_FORWARD)
+    {
+      ctf_err_warn (fp, 1, ECTF_INCOMPLETE,
+                   _("ctf_add_array: index type %lx is incomplete"),
+                   arp->ctr_contents);
+      return (ctf_set_errno (fp, ECTF_INCOMPLETE));
+    }
+
   if ((type = ctf_add_generic (fp, flag, NULL, CTF_K_ARRAY, &dtd)) == CTF_ERR)
     return CTF_ERR;            /* errno is set for us.  */
 
   ssize_t msize, malign, ssize;
   uint32_t kind, vlen, root;
   char *s = NULL;
+  int is_incomplete = 0;
 
   if (!(fp->ctf_flags & LCTF_RDWR))
     return (ctf_set_errno (fp, ECTF_RDONLY));
     {
       /* The unimplemented type, and any type that resolves to it, has no size
         and no alignment: it can correspond to any number of compiler-inserted
-        types.  */
-
+        types.  We allow incomplete types through since they are routinely
+        added to the ends of structures, and can even be added elsewhere in
+        structures by the deduplicator.  They are assumed to be zero-size with
+        no alignment: this is often wrong, but problems can be avoided in this
+        case by explicitly specifying the size of the structure via the _sized
+        functions.  The deduplicator always does this.  */
+
+      msize = 0;
+      malign = 0;
       if (ctf_errno (fp) == ECTF_NONREPRESENTABLE)
-       {
-         msize = 0;
-         malign = 0;
-         ctf_set_errno (fp, 0);
-       }
+       ctf_set_errno (fp, 0);
+      else if (ctf_errno (fp) == ECTF_INCOMPLETE)
+       is_incomplete = 1;
       else
        return -1;              /* errno is set for us.  */
     }
              return -1;        /* errno is set for us.  */
            }
 
+         if (is_incomplete)
+           {
+             ctf_err_warn (fp, 1, ECTF_INCOMPLETE,
+                           _("ctf_add_member_offset: cannot add member %s of "
+                             "incomplete type %lx to struct %lx without "
+                             "specifying explicit offset\n"),
+                           name ? name : _("(unnamed member)"), type, souid);
+             return (ctf_set_errno (fp, ECTF_INCOMPLETE));
+           }
+
          if (ctf_type_encoding (fp, ltype, &linfo) == 0)
            off += linfo.cte_bits;
          else if ((lsize = ctf_type_size (fp, ltype)) > 0)
            off += lsize * CHAR_BIT;
+         else if (lsize == -1 && ctf_errno (fp) == ECTF_INCOMPLETE)
+           {
+             ctf_err_warn (fp, 1, ECTF_INCOMPLETE,
+                           _("ctf_add_member_offset: cannot add member %s of "
+                             "type %lx to struct %lx without specifying "
+                             "explicit offset after member %s of type %lx, "
+                             "which is an incomplete type\n"),
+                           name ? name : _("(unnamed member)"), type, souid,
+                           lmd->dmd_name ? lmd->dmd_name
+                           : _("(unnamed member)"), ltype);
+             return -1;                        /* errno is set for us.  */
+           }
 
          /* Round up the offset of the end of the last member to
             the next byte boundary, convert 'off' to bytes, and
 
       free (bit);
       bit = NULL;
 
-      if (kind != CTF_K_FUNCTION)
+      if (kind != CTF_K_FUNCTION && kind != CTF_K_FORWARD)
        if (asprintf (&bit, " (size 0x%lx)%s",
                      (unsigned long) ctf_type_size (fp, id),
                      nonroot_trailer) < 0)
   char *bit = NULL;
   ctf_encoding_t ep;
   int has_encoding = 0;
+  int opened_paren = 0;
 
   /* Align neatly.  */
 
                    ep.cte_bits, (unsigned long) ctf_type_align (state->cdm_fp,
                                                                 id)) < 0)
        goto oom;
+      opened_paren = 1;
     }
-  else
+  else if (ctf_type_kind (state->cdm_fp, id) != CTF_K_FORWARD)
     {
       if (asprintf (&bit, "[0x%lx] (ID 0x%lx) (kind %i) %s%s%s "
                    "(aligned at 0x%lx", offset, id,
                    (name[0] != 0 && typestr[0] != 0) ? " " : "", name,
                    (unsigned long) ctf_type_align (state->cdm_fp, id)) < 0)
        goto oom;
+      opened_paren = 1;
+    }
+  else /* Forwards have no alignment.  */
+    {
+      if (asprintf (&bit, "[0x%lx] (ID 0x%lx) (kind %i) %s%s%s\n", offset, id,
+                   ctf_type_kind (state->cdm_fp, id), typestr,
+                   (name[0] != 0 && typestr[0] != 0) ? " " : "", name) < 0)
+       goto oom;
     }
 
   *state->cdm_str = str_append (*state->cdm_str, bit);
       bit = NULL;
     }
 
-  *state->cdm_str = str_append (*state->cdm_str, ")\n");
+  if (opened_paren)
+    *state->cdm_str = str_append (*state->cdm_str, ")\n");
   return 0;
 
  oom:
 
    against infinite loops, we implement simplified cycle detection and check
    each link against itself, the previous node, and the topmost node.
 
-   Does not drill down through slices to their contained type.  */
+   Does not drill down through slices to their contained type.
+
+   Callers of this function must not presume that a type it returns must have a
+   valid ctt_size: forwards do not, and must be separately handled.  */
 
 ctf_id_t
 ctf_type_resolve (ctf_dict_t *fp, ctf_id_t type)
 ssize_t
 ctf_type_size (ctf_dict_t *fp, ctf_id_t type)
 {
+  ctf_dict_t *ofp = fp;
   const ctf_type_t *tp;
   ssize_t size;
   ctf_arinfo_t ar;
       if ((size = ctf_get_ctt_size (fp, tp, NULL, NULL)) > 0)
        return size;
 
-      if (ctf_array_info (fp, type, &ar) < 0
-         || (size = ctf_type_size (fp, ar.ctr_contents)) < 0)
+      if (ctf_array_info (ofp, type, &ar) < 0
+         || (size = ctf_type_size (ofp, ar.ctr_contents)) < 0)
        return -1;              /* errno is set for us.  */
 
       return size * ar.ctr_nelems;
 
+    case CTF_K_FORWARD:
+      /* Forwards do not have a meaningful size.  */
+      return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
+
     default: /* including slices of enums, etc */
       return (ctf_get_ctt_size (fp, tp, NULL, NULL));
     }
     case CTF_K_ARRAY:
       {
        ctf_arinfo_t r;
-       if (ctf_array_info (fp, type, &r) < 0)
+       if (ctf_array_info (ofp, type, &r) < 0)
          return -1;            /* errno is set for us.  */
-       return (ctf_type_align (fp, r.ctr_contents));
+       return (ctf_type_align (ofp, r.ctr_contents));
       }
 
     case CTF_K_STRUCT:
                const ctf_member_t *mp = vmp;
                for (; n != 0; n--, mp++)
                  {
-                   ssize_t am = ctf_type_align (fp, mp->ctm_type);
+                   ssize_t am = ctf_type_align (ofp, mp->ctm_type);
                    align = MAX (align, (size_t) am);
                  }
              }
                const ctf_lmember_t *lmp = vmp;
                for (; n != 0; n--, lmp++)
                  {
-                   ssize_t am = ctf_type_align (fp, lmp->ctlm_type);
+                   ssize_t am = ctf_type_align (ofp, lmp->ctlm_type);
                    align = MAX (align, (size_t) am);
                  }
              }
              for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
                   dmd != NULL; dmd = ctf_list_next (dmd))
                {
-                 ssize_t am = ctf_type_align (fp, dmd->dmd_type);
+                 ssize_t am = ctf_type_align (ofp, dmd->dmd_type);
                  align = MAX (align, (size_t) am);
                  if (kind == CTF_K_STRUCT)
                    break;
     case CTF_K_ENUM:
       return fp->ctf_dmodel->ctd_int;
 
+    case CTF_K_FORWARD:
+      /* Forwards do not have a meaningful alignment.  */
+      return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
+
     default:  /* including slices of enums, etc */
       return (ctf_get_ctt_size (fp, tp, NULL, NULL));
     }