]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/input.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / input.c
index 3e67314932a1faf08a7db2fb59fc119bb2a913f2..4b809862e02dc78f63572bf78a5fc413a2b99f6e 100644 (file)
@@ -1,5 +1,5 @@
 /* Data and functions related to line maps and input files.
-   Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   Copyright (C) 2004-2021 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -21,7 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "intl.h"
-#include "diagnostic-core.h"
+#include "diagnostic.h"
 #include "selftest.h"
 #include "cpplib.h"
 
@@ -29,13 +29,50 @@ along with GCC; see the file COPYING3.  If not see
 #define HAVE_ICONV 0
 #endif
 
+/* Input charset configuration.  */
+static const char *default_charset_callback (const char *)
+{
+  return nullptr;
+}
+
+void
+file_cache::initialize_input_context (diagnostic_input_charset_callback ccb,
+                                     bool should_skip_bom)
+{
+  in_context.ccb = (ccb ? ccb : default_charset_callback);
+  in_context.should_skip_bom = should_skip_bom;
+}
+
 /* This is a cache used by get_next_line to store the content of a
    file to be searched for file lines.  */
-struct fcache
+class file_cache_slot
 {
+public:
+  file_cache_slot ();
+  ~file_cache_slot ();
+
+  bool read_line_num (size_t line_num,
+                     char ** line, ssize_t *line_len);
+
+  /* Accessors.  */
+  const char *get_file_path () const { return m_file_path; }
+  unsigned get_use_count () const { return m_use_count; }
+  bool missing_trailing_newline_p () const
+  {
+    return m_missing_trailing_newline;
+  }
+
+  void inc_use_count () { m_use_count++; }
+
+  bool create (const file_cache::input_context &in_context,
+              const char *file_path, FILE *fp, unsigned highest_use_count);
+  void evict ();
+
+ private:
   /* These are information used to store a line boundary.  */
-  struct line_info
+  class line_info
   {
+  public:
     /* The line number.  It starts from 1.  */
     size_t line_num;
 
@@ -58,36 +95,52 @@ struct fcache
     {}
   };
 
+  bool needs_read_p () const;
+  bool needs_grow_p () const;
+  void maybe_grow ();
+  bool read_data ();
+  bool maybe_read_data ();
+  bool get_next_line (char **line, ssize_t *line_len);
+  bool read_next_line (char ** line, ssize_t *line_len);
+  bool goto_next_line ();
+
+  static const size_t buffer_size = 4 * 1024;
+  static const size_t line_record_size = 100;
+
   /* The number of time this file has been accessed.  This is used
      to designate which file cache to evict from the cache
      array.  */
-  unsigned use_count;
+  unsigned m_use_count;
 
   /* The file_path is the key for identifying a particular file in
      the cache.
      For libcpp-using code, the underlying buffer for this field is
      owned by the corresponding _cpp_file within the cpp_reader.  */
-  const char *file_path;
+  const char *m_file_path;
 
-  FILE *fp;
+  FILE *m_fp;
 
   /* This points to the content of the file that we've read so
      far.  */
-  char *data;
+  char *m_data;
+
+  /* The allocated buffer to be freed may start a little earlier than DATA,
+     e.g. if a UTF8 BOM was skipped at the beginning.  */
+  int m_alloc_offset;
 
   /*  The size of the DATA array above.*/
-  size_t size;
+  size_t m_size;
 
   /* The number of bytes read from the underlying file so far.  This
      must be less (or equal) than SIZE above.  */
-  size_t nb_read;
+  size_t m_nb_read;
 
   /* The index of the beginning of the current line.  */
-  size_t line_start_idx;
+  size_t m_line_start_idx;
 
   /* The number of the previous line read.  This starts at 1.  Zero
      means we've read no line so far.  */
-  size_t line_num;
+  size_t m_line_num;
 
   /* This is the total number of lines of the current file.  At the
      moment, we try to get this information from the line map
@@ -97,43 +150,46 @@ struct fcache
      the number of lines before compilation really starts.  For e.g,
      the C front-end, it can happen that we start emitting diagnostics
      before the line map has seen the end of the file.  */
-  size_t total_lines;
+  size_t m_total_lines;
 
   /* Could this file be missing a trailing newline on its final line?
      Initially true (to cope with empty files), set to true/false
      as each line is read.  */
-  bool missing_trailing_newline;
+  bool m_missing_trailing_newline;
 
   /* This is a record of the beginning and end of the lines we've seen
      while reading the file.  This is useful to avoid walking the data
      from the beginning when we are asked to read a line that is
      before LINE_START_IDX above.  Note that the maximum size of this
-     record is fcache_line_record_size, so that the memory consumption
+     record is line_record_size, so that the memory consumption
      doesn't explode.  We thus scale total_lines down to
-     fcache_line_record_size.  */
-  vec<line_info, va_heap> line_record;
+     line_record_size.  */
+  vec<line_info, va_heap> m_line_record;
+
+  void offset_buffer (int offset)
+  {
+    gcc_assert (offset < 0 ? m_alloc_offset + offset >= 0
+               : (size_t) offset <= m_size);
+    gcc_assert (m_data);
+    m_alloc_offset += offset;
+    m_data += offset;
+    m_size -= offset;
+  }
 
-  fcache ();
-  ~fcache ();
 };
 
 /* Current position in real source file.  */
 
 location_t input_location = UNKNOWN_LOCATION;
 
-struct line_maps *line_table;
+class line_maps *line_table;
 
 /* A stashed copy of "line_table" for use by selftest::line_table_test.
    This needs to be a global so that it can be a GC root, and thus
    prevent the stashed copy from being garbage-collected if the GC runs
    during a line_table_test.  */
 
-struct line_maps *saved_line_table;
-
-static fcache *fcache_tab;
-static const size_t fcache_tab_size = 16;
-static const size_t fcache_buffer_size = 4 * 1024;
-static const size_t fcache_line_record_size = 100;
+class line_maps *saved_line_table;
 
 /* Expand the source location LOC into a human readable location.  If
    LOC resolves to a builtin location, the file name of the readable
@@ -147,11 +203,14 @@ static const size_t fcache_line_record_size = 100;
    associated line/column) in the context of a macro expansion, the
    returned location is the first one (while unwinding the macro
    location towards its expansion point) that is in real source
-   code.  */
+   code.
+
+   ASPECT controls which part of the location to use.  */
 
 static expanded_location
-expand_location_1 (source_location loc,
-                  bool expansion_point_p)
+expand_location_1 (location_t loc,
+                  bool expansion_point_p,
+                  enum location_aspect aspect)
 {
   expanded_location xloc;
   const line_map_ordinary *map;
@@ -181,8 +240,36 @@ expand_location_1 (source_location loc,
                                                          loc, NULL);
          lrk = LRK_SPELLING_LOCATION;
        }
-      loc = linemap_resolve_location (line_table, loc,
-                                     lrk, &map);
+      loc = linemap_resolve_location (line_table, loc, lrk, &map);
+
+      /* loc is now either in an ordinary map, or is a reserved location.
+        If it is a compound location, the caret is in a spelling location,
+        but the start/finish might still be a virtual location.
+        Depending of what the caller asked for, we may need to recurse
+        one level in order to resolve any virtual locations in the
+        end-points.  */
+      switch (aspect)
+       {
+       default:
+         gcc_unreachable ();
+         /* Fall through.  */
+       case LOCATION_ASPECT_CARET:
+         break;
+       case LOCATION_ASPECT_START:
+         {
+           location_t start = get_start (loc);
+           if (start != loc)
+             return expand_location_1 (start, expansion_point_p, aspect);
+         }
+         break;
+       case LOCATION_ASPECT_FINISH:
+         {
+           location_t finish = get_finish (loc);
+           if (finish != loc)
+             return expand_location_1 (finish, expansion_point_p, aspect);
+         }
+         break;
+       }
       xloc = linemap_expand_location (line_table, map, loc);
     }
 
@@ -199,8 +286,9 @@ expand_location_1 (source_location loc,
 static void
 diagnostic_file_cache_init (void)
 {
-  if (fcache_tab == NULL)
-    fcache_tab = new fcache[fcache_tab_size];
+  gcc_assert (global_dc);
+  if (global_dc->m_file_cache == NULL)
+    global_dc->m_file_cache = new file_cache ();
 }
 
 /* Free the resources used by the set of cache used for files accessed
@@ -209,10 +297,10 @@ diagnostic_file_cache_init (void)
 void
 diagnostic_file_cache_fini (void)
 {
-  if (fcache_tab)
+  if (global_dc->m_file_cache)
     {
-      delete [] (fcache_tab);
-      fcache_tab = NULL;
+      delete global_dc->m_file_cache;
+      global_dc->m_file_cache = NULL;
     }
 }
 
@@ -225,7 +313,7 @@ static size_t
 total_lines_num (const char *file_path)
 {
   size_t r = 0;
-  source_location l = 0;
+  location_t l = 0;
   if (linemap_get_file_highest_location (line_table, file_path, &l))
     {
       gcc_assert (l >= RESERVED_LOCATION_COUNT);
@@ -239,28 +327,25 @@ total_lines_num (const char *file_path)
    caret diagnostic.  Return the found cached file, or NULL if no
    cached file was found.  */
 
-static fcache*
-lookup_file_in_cache_tab (const char *file_path)
+file_cache_slot *
+file_cache::lookup_file (const char *file_path)
 {
-  if (file_path == NULL)
-    return NULL;
-
-  diagnostic_file_cache_init ();
+  gcc_assert (file_path);
 
   /* This will contain the found cached file.  */
-  fcache *r = NULL;
-  for (unsigned i = 0; i < fcache_tab_size; ++i)
+  file_cache_slot *r = NULL;
+  for (unsigned i = 0; i < num_file_slots; ++i)
     {
-      fcache *c = &fcache_tab[i];
-      if (c->file_path && !strcmp (c->file_path, file_path))
+      file_cache_slot *c = &m_file_slots[i];
+      if (c->get_file_path () && !strcmp (c->get_file_path (), file_path))
        {
-         ++c->use_count;
+         c->inc_use_count ();
          r = c;
        }
     }
 
   if (r)
-    ++r->use_count;
+    r->inc_use_count ();
 
   return r;
 }
@@ -274,22 +359,39 @@ diagnostics_file_cache_forcibly_evict_file (const char *file_path)
 {
   gcc_assert (file_path);
 
-  fcache *r = lookup_file_in_cache_tab (file_path);
+  if (!global_dc->m_file_cache)
+    return;
+
+  global_dc->m_file_cache->forcibly_evict_file (file_path);
+}
+
+void
+file_cache::forcibly_evict_file (const char *file_path)
+{
+  gcc_assert (file_path);
+
+  file_cache_slot *r = lookup_file (file_path);
   if (!r)
     /* Not found.  */
     return;
 
-  r->file_path = NULL;
-  if (r->fp)
-    fclose (r->fp);
-  r->fp = NULL;
-  r->nb_read = 0;
-  r->line_start_idx = 0;
-  r->line_num = 0;
-  r->line_record.truncate (0);
-  r->use_count = 0;
-  r->total_lines = 0;
-  r->missing_trailing_newline = true;
+  r->evict ();
+}
+
+void
+file_cache_slot::evict ()
+{
+  m_file_path = NULL;
+  if (m_fp)
+    fclose (m_fp);
+  m_fp = NULL;
+  m_nb_read = 0;
+  m_line_start_idx = 0;
+  m_line_num = 0;
+  m_line_record.truncate (0);
+  m_use_count = 0;
+  m_total_lines = 0;
+  m_missing_trailing_newline = true;
 }
 
 /* Return the file cache that has been less used, recently, or the
@@ -297,26 +399,26 @@ diagnostics_file_cache_forcibly_evict_file (const char *file_path)
    *HIGHEST_USE_COUNT is set to the highest use count of the entries
    in the cache table.  */
 
-static fcache*
-evicted_cache_tab_entry (unsigned *highest_use_count)
+file_cache_slot*
+file_cache::evicted_cache_tab_entry (unsigned *highest_use_count)
 {
   diagnostic_file_cache_init ();
 
-  fcache *to_evict = &fcache_tab[0];
-  unsigned huc = to_evict->use_count;
-  for (unsigned i = 1; i < fcache_tab_size; ++i)
+  file_cache_slot *to_evict = &m_file_slots[0];
+  unsigned huc = to_evict->get_use_count ();
+  for (unsigned i = 1; i < num_file_slots; ++i)
     {
-      fcache *c = &fcache_tab[i];
-      bool c_is_empty = (c->file_path == NULL);
+      file_cache_slot *c = &m_file_slots[i];
+      bool c_is_empty = (c->get_file_path () == NULL);
 
-      if (c->use_count < to_evict->use_count
-         || (to_evict->file_path && c_is_empty))
+      if (c->get_use_count () < to_evict->get_use_count ()
+         || (to_evict->get_file_path () && c_is_empty))
        /* We evict C because it's either an entry with a lower use
           count or one that is empty.  */
        to_evict = c;
 
-      if (huc < c->use_count)
-       huc = c->use_count;
+      if (huc < c->get_use_count ())
+       huc = c->get_use_count ();
 
       if (c_is_empty)
        /* We've reached the end of the cache; subsequent elements are
@@ -334,10 +436,10 @@ evicted_cache_tab_entry (unsigned *highest_use_count)
    accessed by caret diagnostic.  This cache is added to an array of
    cache and can be retrieved by lookup_file_in_cache_tab.  This
    function returns the created cache.  Note that only the last
-   fcache_tab_size files are cached.  */
+   num_file_slots files are cached.  */
 
-static fcache*
-add_file_to_cache_tab (const char *file_path)
+file_cache_slot*
+file_cache::add_file (const char *file_path)
 {
 
   FILE *fp = fopen (file_path, "r");
@@ -345,22 +447,80 @@ add_file_to_cache_tab (const char *file_path)
     return NULL;
 
   unsigned highest_use_count = 0;
-  fcache *r = evicted_cache_tab_entry (&highest_use_count);
-  r->file_path = file_path;
-  if (r->fp)
-    fclose (r->fp);
-  r->fp = fp;
-  r->nb_read = 0;
-  r->line_start_idx = 0;
-  r->line_num = 0;
-  r->line_record.truncate (0);
+  file_cache_slot *r = evicted_cache_tab_entry (&highest_use_count);
+  if (!r->create (in_context, file_path, fp, highest_use_count))
+    return NULL;
+  return r;
+}
+
+/* Populate this slot for use on FILE_PATH and FP, dropping any
+   existing cached content within it.  */
+
+bool
+file_cache_slot::create (const file_cache::input_context &in_context,
+                        const char *file_path, FILE *fp,
+                        unsigned highest_use_count)
+{
+  m_file_path = file_path;
+  if (m_fp)
+    fclose (m_fp);
+  m_fp = fp;
+  if (m_alloc_offset)
+    offset_buffer (-m_alloc_offset);
+  m_nb_read = 0;
+  m_line_start_idx = 0;
+  m_line_num = 0;
+  m_line_record.truncate (0);
   /* Ensure that this cache entry doesn't get evicted next time
      add_file_to_cache_tab is called.  */
-  r->use_count = ++highest_use_count;
-  r->total_lines = total_lines_num (file_path);
-  r->missing_trailing_newline = true;
+  m_use_count = ++highest_use_count;
+  m_total_lines = total_lines_num (file_path);
+  m_missing_trailing_newline = true;
 
-  return r;
+
+  /* Check the input configuration to determine if we need to do any
+     transformations, such as charset conversion or BOM skipping.  */
+  if (const char *input_charset = in_context.ccb (file_path))
+    {
+      /* Need a full-blown conversion of the input charset.  */
+      fclose (m_fp);
+      m_fp = NULL;
+      const cpp_converted_source cs
+       = cpp_get_converted_source (file_path, input_charset);
+      if (!cs.data)
+       return false;
+      if (m_data)
+       XDELETEVEC (m_data);
+      m_data = cs.data;
+      m_nb_read = m_size = cs.len;
+      m_alloc_offset = cs.data - cs.to_free;
+    }
+  else if (in_context.should_skip_bom)
+    {
+      if (read_data ())
+       {
+         const int offset = cpp_check_utf8_bom (m_data, m_nb_read);
+         offset_buffer (offset);
+         m_nb_read -= offset;
+       }
+    }
+
+  return true;
+}
+
+/* file_cache's ctor.  */
+
+file_cache::file_cache ()
+: m_file_slots (new file_cache_slot[num_file_slots])
+{
+  initialize_input_context (nullptr, false);
+}
+
+/* file_cache's dtor.  */
+
+file_cache::~file_cache ()
+{
+  delete[] m_file_slots;
 }
 
 /* Lookup the cache used for the content of a given file accessed by
@@ -368,41 +528,42 @@ add_file_to_cache_tab (const char *file_path)
    for this file, add it to the array of cached file and return
    it.  */
 
-static fcache*
-lookup_or_add_file_to_cache_tab (const char *file_path)
+file_cache_slot*
+file_cache::lookup_or_add_file (const char *file_path)
 {
-  fcache *r = lookup_file_in_cache_tab (file_path);
+  file_cache_slot *r = lookup_file (file_path);
   if (r == NULL)
-    r = add_file_to_cache_tab (file_path);
+    r = add_file (file_path);
   return r;
 }
 
 /* Default constructor for a cache of file used by caret
    diagnostic.  */
 
-fcache::fcache ()
-: use_count (0), file_path (NULL), fp (NULL), data (0),
-  size (0), nb_read (0), line_start_idx (0), line_num (0),
-  total_lines (0), missing_trailing_newline (true)
+file_cache_slot::file_cache_slot ()
+: m_use_count (0), m_file_path (NULL), m_fp (NULL), m_data (0),
+  m_alloc_offset (0), m_size (0), m_nb_read (0), m_line_start_idx (0),
+  m_line_num (0), m_total_lines (0), m_missing_trailing_newline (true)
 {
-  line_record.create (0);
+  m_line_record.create (0);
 }
 
 /* Destructor for a cache of file used by caret diagnostic.  */
 
-fcache::~fcache ()
+file_cache_slot::~file_cache_slot ()
 {
-  if (fp)
+  if (m_fp)
     {
-      fclose (fp);
-      fp = NULL;
+      fclose (m_fp);
+      m_fp = NULL;
     }
-  if (data)
+  if (m_data)
     {
-      XDELETEVEC (data);
-      data = 0;
+      offset_buffer (-m_alloc_offset);
+      XDELETEVEC (m_data);
+      m_data = 0;
     }
-  line_record.release ();
+  m_line_record.release ();
 }
 
 /* Returns TRUE iff the cache would need to be filled with data coming
@@ -410,55 +571,66 @@ fcache::~fcache ()
    current line is empty.  Note that if the cache is full, it would
    need to be extended and filled again.  */
 
-static bool
-needs_read (fcache *c)
+bool
+file_cache_slot::needs_read_p () const
 {
-  return (c->nb_read == 0
-         || c->nb_read == c->size
-         || (c->line_start_idx >= c->nb_read - 1));
+  return m_fp && (m_nb_read == 0
+         || m_nb_read == m_size
+         || (m_line_start_idx >= m_nb_read - 1));
 }
 
 /*  Return TRUE iff the cache is full and thus needs to be
     extended.  */
 
-static bool
-needs_grow (fcache *c)
+bool
+file_cache_slot::needs_grow_p () const
 {
-  return c->nb_read == c->size;
+  return m_nb_read == m_size;
 }
 
 /* Grow the cache if it needs to be extended.  */
 
-static void
-maybe_grow (fcache *c)
+void
+file_cache_slot::maybe_grow ()
 {
-  if (!needs_grow (c))
+  if (!needs_grow_p ())
     return;
 
-  size_t size = c->size == 0 ? fcache_buffer_size : c->size * 2;
-  c->data = XRESIZEVEC (char, c->data, size);
-  c->size = size;
+  if (!m_data)
+    {
+      gcc_assert (m_size == 0 && m_alloc_offset == 0);
+      m_size = buffer_size;
+      m_data = XNEWVEC (char, m_size);
+    }
+  else
+    {
+      const int offset = m_alloc_offset;
+      offset_buffer (-offset);
+      m_size *= 2;
+      m_data = XRESIZEVEC (char, m_data, m_size);
+      offset_buffer (offset);
+    }
 }
 
 /*  Read more data into the cache.  Extends the cache if need be.
     Returns TRUE iff new data could be read.  */
 
-static bool
-read_data (fcache *c)
+bool
+file_cache_slot::read_data ()
 {
-  if (feof (c->fp) || ferror (c->fp))
+  if (feof (m_fp) || ferror (m_fp))
     return false;
 
-  maybe_grow (c);
+  maybe_grow ();
 
-  char * from = c->data + c->nb_read;
-  size_t to_read = c->size - c->nb_read;
-  size_t nb_read = fread (from, 1, to_read, c->fp);
+  char * from = m_data + m_nb_read;
+  size_t to_read = m_size - m_nb_read;
+  size_t nb_read = fread (from, 1, to_read, m_fp);
 
-  if (ferror (c->fp))
+  if (ferror (m_fp))
     return false;
 
-  c->nb_read += nb_read;
+  m_nb_read += nb_read;
   return !!nb_read;
 }
 
@@ -466,12 +638,12 @@ read_data (fcache *c)
    coming from the file FP.  Return TRUE iff the cache was filled with
    mode data.  */
 
-static bool
-maybe_read_data (fcache *c)
+bool
+file_cache_slot::maybe_read_data ()
 {
-  if (!needs_read (c))
+  if (!needs_read_p ())
     return false;
-  return read_data (c);
+  return read_data ();
 }
 
 /* Read a new line from file FP, using C as a cache for the data
@@ -484,18 +656,18 @@ maybe_read_data (fcache *c)
    otherwise.  Note that subsequent calls to get_next_line might
    make the content of *LINE invalid.  */
 
-static bool
-get_next_line (fcache *c, char **line, ssize_t *line_len)
+bool
+file_cache_slot::get_next_line (char **line, ssize_t *line_len)
 {
   /* Fill the cache with data to process.  */
-  maybe_read_data (c);
+  maybe_read_data ();
 
-  size_t remaining_size = c->nb_read - c->line_start_idx;
+  size_t remaining_size = m_nb_read - m_line_start_idx;
   if (remaining_size == 0)
     /* There is no more data to process.  */
     return false;
 
-  char *line_start = c->data + c->line_start_idx;
+  char *line_start = m_data + m_line_start_idx;
 
   char *next_line_start = NULL;
   size_t len = 0;
@@ -505,10 +677,10 @@ get_next_line (fcache *c, char **line, ssize_t *line_len)
       /* We haven't found the end-of-line delimiter in the cache.
         Fill the cache with more data from the file and look for the
         '\n'.  */
-      while (maybe_read_data (c))
+      while (maybe_read_data ())
        {
-         line_start = c->data + c->line_start_idx;
-         remaining_size = c->nb_read - c->line_start_idx;
+         line_start = m_data + m_line_start_idx;
+         remaining_size = m_nb_read - m_line_start_idx;
          line_end = (char *) memchr (line_start, '\n', remaining_size);
          if (line_end != NULL)
            {
@@ -524,19 +696,19 @@ get_next_line (fcache *c, char **line, ssize_t *line_len)
             of when the line ends up with a '\n' and line_end points to
             that terminal '\n'.  That consistency is useful below in
             the len calculation.  */
-         line_end = c->data + c->nb_read ;
-         c->missing_trailing_newline = true;
+         line_end = m_data + m_nb_read ;
+         m_missing_trailing_newline = true;
        }
       else
-       c->missing_trailing_newline = false;
+       m_missing_trailing_newline = false;
     }
   else
     {
       next_line_start = line_end + 1;
-      c->missing_trailing_newline = false;
+      m_missing_trailing_newline = false;
     }
 
-  if (ferror (c->fp))
+  if (m_fp && ferror (m_fp))
     return false;
 
   /* At this point, we've found the end of the of line.  It either
@@ -546,54 +718,56 @@ get_next_line (fcache *c, char **line, ssize_t *line_len)
 
   len = line_end - line_start;
 
-  if (c->line_start_idx < c->nb_read)
+  if (m_line_start_idx < m_nb_read)
     *line = line_start;
 
-  ++c->line_num;
+  ++m_line_num;
 
   /* Before we update our line record, make sure the hint about the
      total number of lines of the file is correct.  If it's not, then
      we give up recording line boundaries from now on.  */
   bool update_line_record = true;
-  if (c->line_num > c->total_lines)
+  if (m_line_num > m_total_lines)
     update_line_record = false;
 
     /* Now update our line record so that re-reading lines from the
-     before c->line_start_idx is faster.  */
+     before m_line_start_idx is faster.  */
   if (update_line_record
-      && c->line_record.length () < fcache_line_record_size)
+      && m_line_record.length () < line_record_size)
     {
       /* If the file lines fits in the line record, we just record all
         its lines ...*/
-      if (c->total_lines <= fcache_line_record_size
-         && c->line_num > c->line_record.length ())
-       c->line_record.safe_push (fcache::line_info (c->line_num,
-                                                c->line_start_idx,
-                                                line_end - c->data));
-      else if (c->total_lines > fcache_line_record_size)
+      if (m_total_lines <= line_record_size
+         && m_line_num > m_line_record.length ())
+       m_line_record.safe_push
+         (file_cache_slot::line_info (m_line_num,
+                                      m_line_start_idx,
+                                      line_end - m_data));
+      else if (m_total_lines > line_record_size)
        {
          /* ... otherwise, we just scale total_lines down to
-            (fcache_line_record_size lines.  */
-         size_t n = (c->line_num * fcache_line_record_size) / c->total_lines;
-         if (c->line_record.length () == 0
-             || n >= c->line_record.length ())
-           c->line_record.safe_push (fcache::line_info (c->line_num,
-                                                    c->line_start_idx,
-                                                    line_end - c->data));
+            (line_record_size lines.  */
+         size_t n = (m_line_num * line_record_size) / m_total_lines;
+         if (m_line_record.length () == 0
+             || n >= m_line_record.length ())
+           m_line_record.safe_push
+             (file_cache_slot::line_info (m_line_num,
+                                          m_line_start_idx,
+                                          line_end - m_data));
        }
     }
 
-  /* Update c->line_start_idx so that it points to the next line to be
+  /* Update m_line_start_idx so that it points to the next line to be
      read.  */
   if (next_line_start)
-    c->line_start_idx = next_line_start - c->data;
+    m_line_start_idx = next_line_start - m_data;
   else
     /* We didn't find any terminal '\n'.  Let's consider that the end
        of line is the end of the data in the cache.  The next
        invocation of get_next_line will either read more data from the
        underlying file or return false early because we've reached the
        end of the file.  */
-    c->line_start_idx = c->nb_read;
+    m_line_start_idx = m_nb_read;
 
   *line_len = len;
 
@@ -606,13 +780,13 @@ get_next_line (fcache *c, char **line, ssize_t *line_len)
    copying from the cache involved.  Return TRUE upon successful
    completion.  */
 
-static bool
-goto_next_line (fcache *cache)
+bool
+file_cache_slot::goto_next_line ()
 {
   char *l;
   ssize_t len;
 
-  return get_next_line (cache, &l, &len);
+  return get_next_line (&l, &len);
 }
 
 /* Read an arbitrary line number LINE_NUM from the file cached in C.
@@ -622,54 +796,54 @@ goto_next_line (fcache *cache)
    *LINE is only valid until the next call of read_line_num.
    This function returns bool if a line was read.  */
 
-static bool
-read_line_num (fcache *c, size_t line_num,
-              char **line, ssize_t *line_len)
+bool
+file_cache_slot::read_line_num (size_t line_num,
+                      char ** line, ssize_t *line_len)
 {
   gcc_assert (line_num > 0);
 
-  if (line_num <= c->line_num)
+  if (line_num <= m_line_num)
     {
-      /* We've been asked to read lines that are before c->line_num.
+      /* We've been asked to read lines that are before m_line_num.
         So lets use our line record (if it's not empty) to try to
         avoid re-reading the file from the beginning again.  */
 
-      if (c->line_record.is_empty ())
+      if (m_line_record.is_empty ())
        {
-         c->line_start_idx = 0;
-         c->line_num = 0;
+         m_line_start_idx = 0;
+         m_line_num = 0;
        }
       else
        {
-         fcache::line_info *i = NULL;
-         if (c->total_lines <= fcache_line_record_size)
+         file_cache_slot::line_info *i = NULL;
+         if (m_total_lines <= line_record_size)
            {
              /* In languages where the input file is not totally
-                preprocessed up front, the c->total_lines hint
+                preprocessed up front, the m_total_lines hint
                 can be smaller than the number of lines of the
                 file.  In that case, only the first
-                c->total_lines have been recorded.
+                m_total_lines have been recorded.
 
-                Otherwise, the first c->total_lines we've read have
+                Otherwise, the first m_total_lines we've read have
                 their start/end recorded here.  */
-             i = (line_num <= c->total_lines)
-               ? &c->line_record[line_num - 1]
-               : &c->line_record[c->total_lines - 1];
+             i = (line_num <= m_total_lines)
+               ? &m_line_record[line_num - 1]
+               : &m_line_record[m_total_lines - 1];
              gcc_assert (i->line_num <= line_num);
            }
          else
            {
              /*  So the file had more lines than our line record
                  size.  Thus the number of lines we've recorded has
-                 been scaled down to fcache_line_reacord_size.  Let's
+                 been scaled down to line_record_size.  Let's
                  pick the start/end of the recorded line that is
                  closest to line_num.  */
-             size_t n = (line_num <= c->total_lines)
-               ? line_num * fcache_line_record_size / c->total_lines
-               : c ->line_record.length () - 1;
-             if (n < c->line_record.length ())
+             size_t n = (line_num <= m_total_lines)
+               ? line_num * line_record_size / m_total_lines
+               : m_line_record.length () - 1;
+             if (n < m_line_record.length ())
                {
-                 i = &c->line_record[n];
+                 i = &m_line_record[n];
                  gcc_assert (i->line_num <= line_num);
                }
            }
@@ -677,62 +851,65 @@ read_line_num (fcache *c, size_t line_num,
          if (i && i->line_num == line_num)
            {
              /* We have the start/end of the line.  */
-             *line = c->data + i->start_pos;
+             *line = m_data + i->start_pos;
              *line_len = i->end_pos - i->start_pos;
              return true;
            }
 
          if (i)
            {
-             c->line_start_idx = i->start_pos;
-             c->line_num = i->line_num - 1;
+             m_line_start_idx = i->start_pos;
+             m_line_num = i->line_num - 1;
            }
          else
            {
-             c->line_start_idx = 0;
-             c->line_num = 0;
+             m_line_start_idx = 0;
+             m_line_num = 0;
            }
        }
     }
 
-  /*  Let's walk from line c->line_num up to line_num - 1, without
+  /*  Let's walk from line m_line_num up to line_num - 1, without
       copying any line.  */
-  while (c->line_num < line_num - 1)
-    if (!goto_next_line (c))
+  while (m_line_num < line_num - 1)
+    if (!goto_next_line ())
       return false;
 
   /* The line we want is the next one.  Let's read and copy it back to
      the caller.  */
-  return get_next_line (c, line, line_len);
+  return get_next_line (line, line_len);
 }
 
 /* Return the physical source line that corresponds to FILE_PATH/LINE.
    The line is not nul-terminated.  The returned pointer is only
    valid until the next call of location_get_source_line.
    Note that the line can contain several null characters,
-   so LINE_LEN, if non-null, points to the actual length of the line.
-   If the function fails, NULL is returned.  */
+   so the returned value's length has the actual length of the line.
+   If the function fails, a NULL char_span is returned.  */
 
-const char *
-location_get_source_line (const char *file_path, int line,
-                         int *line_len)
+char_span
+location_get_source_line (const char *file_path, int line)
 {
   char *buffer = NULL;
   ssize_t len;
 
   if (line == 0)
-    return NULL;
+    return char_span (NULL, 0);
 
-  fcache *c = lookup_or_add_file_to_cache_tab (file_path);
-  if (c == NULL)
-    return NULL;
+  if (file_path == NULL)
+    return char_span (NULL, 0);
 
-  bool read = read_line_num (c, line, &buffer, &len);
+  diagnostic_file_cache_init ();
 
-  if (read && line_len)
-    *line_len = len;
+  file_cache_slot *c = global_dc->m_file_cache->lookup_or_add_file (file_path);
+  if (c == NULL)
+    return char_span (NULL, 0);
+
+  bool read = c->read_line_num (line, &buffer, &len);
+  if (!read)
+    return char_span (NULL, 0);
 
-  return read ? buffer : NULL;
+  return char_span (buffer, len);
 }
 
 /* Determine if FILE_PATH missing a trailing newline on its final line.
@@ -742,11 +919,13 @@ location_get_source_line (const char *file_path, int line,
 bool
 location_missing_trailing_newline (const char *file_path)
 {
-  fcache *c = lookup_or_add_file_to_cache_tab (file_path);
+  diagnostic_file_cache_init ();
+
+  file_cache_slot *c = global_dc->m_file_cache->lookup_or_add_file (file_path);
   if (c == NULL)
     return false;
 
-  return c->missing_trailing_newline;
+  return c->missing_trailing_newline_p ();
 }
 
 /* Test if the location originates from the spelling location of a
@@ -757,7 +936,7 @@ location_missing_trailing_newline (const char *file_path)
    function would return true if passed a token "4" that is the result
    of the expansion of the built-in __LINE__ macro.  */
 bool
-is_location_from_builtin_token (source_location loc)
+is_location_from_builtin_token (location_t loc)
 {
   const line_map_ordinary *map = NULL;
   loc = linemap_resolve_location (line_table, loc,
@@ -771,9 +950,10 @@ is_location_from_builtin_token (source_location loc)
    readable location is set to the string "<built-in>".  */
 
 expanded_location
-expand_location (source_location loc)
+expand_location (location_t loc)
 {
-  return expand_location_1 (loc, /*expansion_point_p=*/true);
+  return expand_location_1 (loc, /*expansion_point_p=*/true,
+                           LOCATION_ASPECT_CARET);
 }
 
 /* Expand the source location LOC into a human readable location.  If
@@ -783,24 +963,26 @@ expand_location (source_location loc)
    "<built-in>".  */
 
 expanded_location
-expand_location_to_spelling_point (source_location loc)
+expand_location_to_spelling_point (location_t loc,
+                                  enum location_aspect aspect)
 {
-  return expand_location_1 (loc, /*expansion_point_p=*/false);
+  return expand_location_1 (loc, /*expansion_point_p=*/false, aspect);
 }
 
 /* The rich_location class within libcpp requires a way to expand
-   source_location instances, and relies on the client code
+   location_t instances, and relies on the client code
    providing a symbol named
      linemap_client_expand_location_to_spelling_point
    to do this.
 
    This is the implementation for libcommon.a (all host binaries),
-   which simply calls into expand_location_to_spelling_point.  */
+   which simply calls into expand_location_1.  */
 
 expanded_location
-linemap_client_expand_location_to_spelling_point (source_location loc)
+linemap_client_expand_location_to_spelling_point (location_t loc,
+                                                 enum location_aspect aspect)
 {
-  return expand_location_to_spelling_point (loc);
+  return expand_location_1 (loc, /*expansion_point_p=*/false, aspect);
 }
 
 
@@ -816,8 +998,8 @@ linemap_client_expand_location_to_spelling_point (source_location loc)
    warning_at, the diagnostic would be suppressed (unless
    -Wsystem-headers).  */
 
-source_location
-expansion_point_location_if_in_system_header (source_location location)
+location_t
+expansion_point_location_if_in_system_header (location_t location)
 {
   if (in_system_header_at (location))
     location = linemap_resolve_location (line_table, location,
@@ -829,8 +1011,8 @@ expansion_point_location_if_in_system_header (source_location location)
 /* If LOCATION is a virtual location for a token coming from the expansion
    of a macro, unwind to the location of the expansion point of the macro.  */
 
-source_location
-expansion_point_location (source_location location)
+location_t
+expansion_point_location (location_t location)
 {
   return linemap_resolve_location (line_table, location,
                                   LRK_MACRO_EXPANSION_POINT, NULL);
@@ -864,30 +1046,30 @@ make_location (location_t caret, location_t start, location_t finish)
   return combined_loc;
 }
 
-#define ONE_K 1024
-#define ONE_M (ONE_K * ONE_K)
-
-/* Display a number as an integer multiple of either:
-   - 1024, if said integer is >= to 10 K (in base 2)
-   - 1024 * 1024, if said integer is >= 10 M in (base 2)
- */
-#define SCALE(x) ((unsigned long) ((x) < 10 * ONE_K \
-                 ? (x) \
-                 : ((x) < 10 * ONE_M \
-                    ? (x) / ONE_K \
-                    : (x) / ONE_M)))
+/* Same as above, but taking a source range rather than two locations.  */
 
-/* For a given integer, display either:
-   - the character 'k', if the number is higher than 10 K (in base 2)
-     but strictly lower than 10 M (in base 2)
-   - the character 'M' if the number is higher than 10 M (in base2)
-   - the charcter ' ' if the number is strictly lower  than 10 K  */
-#define STAT_LABEL(x) ((x) < 10 * ONE_K ? ' ' : ((x) < 10 * ONE_M ? 'k' : 'M'))
+location_t
+make_location (location_t caret, source_range src_range)
+{
+  location_t pure_loc = get_pure_location (caret);
+  return COMBINE_LOCATION_DATA (line_table, pure_loc, src_range, NULL);
+}
 
-/* Display an integer amount as multiple of 1K or 1M (in base 2).
-   Display the correct unit (either k, M, or ' ') after the amout, as
-   well.  */
-#define FORMAT_AMOUNT(size) SCALE (size), STAT_LABEL (size)
+/* An expanded_location stores the column in byte units.  This function
+   converts that column to display units.  That requires reading the associated
+   source line in order to calculate the display width.  If that cannot be done
+   for any reason, then returns the byte column as a fallback.  */
+int
+location_compute_display_column (expanded_location exploc, int tabstop)
+{
+  if (!(exploc.file && *exploc.file && exploc.line && exploc.column))
+    return exploc.column;
+  char_span line = location_get_source_line (exploc.file, exploc.line);
+  /* If line is NULL, this function returns exploc.column which is the
+     desired fallback.  */
+  return cpp_byte_column_to_display_column (line.get_buffer (), line.length (),
+                                           exploc.column, tabstop);
+}
 
 /* Dump statistics to stderr about the memory usage of the line_table
    set of line maps.  This also displays some statistics about macro
@@ -923,57 +1105,45 @@ dump_line_table_statistics (void)
              s.num_macro_tokens / s.num_expanded_macros);
   fprintf (stderr,
            "\nLine Table allocations during the "
-           "compilation process\n");
-  fprintf (stderr, "Number of ordinary maps used:        %5ld%c\n",
-           SCALE (s.num_ordinary_maps_used),
-           STAT_LABEL (s.num_ordinary_maps_used));
-  fprintf (stderr, "Ordinary map used size:              %5ld%c\n",
-           SCALE (s.ordinary_maps_used_size),
-           STAT_LABEL (s.ordinary_maps_used_size));
-  fprintf (stderr, "Number of ordinary maps allocated:   %5ld%c\n",
-           SCALE (s.num_ordinary_maps_allocated),
-           STAT_LABEL (s.num_ordinary_maps_allocated));
-  fprintf (stderr, "Ordinary maps allocated size:        %5ld%c\n",
-           SCALE (s.ordinary_maps_allocated_size),
-           STAT_LABEL (s.ordinary_maps_allocated_size));
-  fprintf (stderr, "Number of macro maps used:           %5ld%c\n",
-           SCALE (s.num_macro_maps_used),
-           STAT_LABEL (s.num_macro_maps_used));
-  fprintf (stderr, "Macro maps used size:                %5ld%c\n",
-           SCALE (s.macro_maps_used_size),
-           STAT_LABEL (s.macro_maps_used_size));
-  fprintf (stderr, "Macro maps locations size:           %5ld%c\n",
-           SCALE (s.macro_maps_locations_size),
-           STAT_LABEL (s.macro_maps_locations_size));
-  fprintf (stderr, "Macro maps size:                     %5ld%c\n",
-           SCALE (macro_maps_size),
-           STAT_LABEL (macro_maps_size));
-  fprintf (stderr, "Duplicated maps locations size:      %5ld%c\n",
-           SCALE (s.duplicated_macro_maps_locations_size),
-           STAT_LABEL (s.duplicated_macro_maps_locations_size));
-  fprintf (stderr, "Total allocated maps size:           %5ld%c\n",
-           SCALE (total_allocated_map_size),
-           STAT_LABEL (total_allocated_map_size));
-  fprintf (stderr, "Total used maps size:                %5ld%c\n",
-           SCALE (total_used_map_size),
-           STAT_LABEL (total_used_map_size));
-  fprintf (stderr, "Ad-hoc table size:                   %5ld%c\n",
-          SCALE (s.adhoc_table_size),
-          STAT_LABEL (s.adhoc_table_size));
-  fprintf (stderr, "Ad-hoc table entries used:           %5ld\n",
-          s.adhoc_table_entries_used);
-  fprintf (stderr, "optimized_ranges: %i\n",
-          line_table->num_optimized_ranges);
-  fprintf (stderr, "unoptimized_ranges: %i\n",
-          line_table->num_unoptimized_ranges);
+          "compilation process\n");
+  fprintf (stderr, "Number of ordinary maps used:        " PRsa (5) "\n",
+          SIZE_AMOUNT (s.num_ordinary_maps_used));
+  fprintf (stderr, "Ordinary map used size:              " PRsa (5) "\n",
+          SIZE_AMOUNT (s.ordinary_maps_used_size));
+  fprintf (stderr, "Number of ordinary maps allocated:   " PRsa (5) "\n",
+          SIZE_AMOUNT (s.num_ordinary_maps_allocated));
+  fprintf (stderr, "Ordinary maps allocated size:        " PRsa (5) "\n",
+          SIZE_AMOUNT (s.ordinary_maps_allocated_size));
+  fprintf (stderr, "Number of macro maps used:           " PRsa (5) "\n",
+          SIZE_AMOUNT (s.num_macro_maps_used));
+  fprintf (stderr, "Macro maps used size:                " PRsa (5) "\n",
+          SIZE_AMOUNT (s.macro_maps_used_size));
+  fprintf (stderr, "Macro maps locations size:           " PRsa (5) "\n",
+          SIZE_AMOUNT (s.macro_maps_locations_size));
+  fprintf (stderr, "Macro maps size:                     " PRsa (5) "\n",
+          SIZE_AMOUNT (macro_maps_size));
+  fprintf (stderr, "Duplicated maps locations size:      " PRsa (5) "\n",
+          SIZE_AMOUNT (s.duplicated_macro_maps_locations_size));
+  fprintf (stderr, "Total allocated maps size:           " PRsa (5) "\n",
+          SIZE_AMOUNT (total_allocated_map_size));
+  fprintf (stderr, "Total used maps size:                " PRsa (5) "\n",
+          SIZE_AMOUNT (total_used_map_size));
+  fprintf (stderr, "Ad-hoc table size:                   " PRsa (5) "\n",
+          SIZE_AMOUNT (s.adhoc_table_size));
+  fprintf (stderr, "Ad-hoc table entries used:           " PRsa (5) "\n",
+          SIZE_AMOUNT (s.adhoc_table_entries_used));
+  fprintf (stderr, "optimized_ranges:                    " PRsa (5) "\n",
+          SIZE_AMOUNT (line_table->num_optimized_ranges));
+  fprintf (stderr, "unoptimized_ranges:                  " PRsa (5) "\n",
+          SIZE_AMOUNT (line_table->num_unoptimized_ranges));
 
   fprintf (stderr, "\n");
 }
 
 /* Get location one beyond the final location in ordinary map IDX.  */
 
-static source_location
-get_end_location (struct line_maps *set, unsigned int idx)
+static location_t
+get_end_location (class line_maps *set, unsigned int idx)
 {
   if (idx == LINEMAPS_ORDINARY_USED (set) - 1)
     return set->highest_location;
@@ -997,37 +1167,37 @@ write_digit (FILE *stream, int digit)
 static void
 write_digit_row (FILE *stream, int indent,
                 const line_map_ordinary *map,
-                source_location loc, int max_col, int divisor)
+                location_t loc, int max_col, int divisor)
 {
   fprintf (stream, "%*c", indent, ' ');
   fprintf (stream, "|");
   for (int column = 1; column < max_col; column++)
     {
-      source_location column_loc = loc + (column << map->m_range_bits);
+      location_t column_loc = loc + (column << map->m_range_bits);
       write_digit (stream, column_loc / divisor);
     }
   fprintf (stream, "\n");
 }
 
 /* Write a half-closed (START) / half-open (END) interval of
-   source_location to STREAM.  */
+   location_t to STREAM.  */
 
 static void
 dump_location_range (FILE *stream,
-                    source_location start, source_location end)
+                    location_t start, location_t end)
 {
   fprintf (stream,
-          "  source_location interval: %u <= loc < %u\n",
+          "  location_t interval: %u <= loc < %u\n",
           start, end);
 }
 
 /* Write a labelled description of a half-closed (START) / half-open (END)
-   interval of source_location to STREAM.  */
+   interval of location_t to STREAM.  */
 
 static void
 dump_labelled_location_range (FILE *stream,
                              const char *name,
-                             source_location start, source_location end)
+                             location_t start, location_t end)
 {
   fprintf (stream, "%s\n", name);
   dump_location_range (stream, start, end);
@@ -1046,7 +1216,7 @@ dump_location_info (FILE *stream)
   /* Visualize the ordinary line_map instances, rendering the sources. */
   for (unsigned int idx = 0; idx < LINEMAPS_ORDINARY_USED (line_table); idx++)
     {
-      source_location end_location = get_end_location (line_table, idx);
+      location_t end_location = get_end_location (line_table, idx);
       /* half-closed: doesn't include this one. */
 
       const line_map_ordinary *map
@@ -1063,9 +1233,40 @@ dump_location_info (FILE *stream)
               map->m_column_and_range_bits - map->m_range_bits);
       fprintf (stream, "  range bits: %i\n",
               map->m_range_bits);
+      const char * reason;
+      switch (map->reason) {
+      case LC_ENTER:
+       reason = "LC_ENTER";
+       break;
+      case LC_LEAVE:
+       reason = "LC_LEAVE";
+       break;
+      case LC_RENAME:
+       reason = "LC_RENAME";
+       break;
+      case LC_RENAME_VERBATIM:
+       reason = "LC_RENAME_VERBATIM";
+       break;
+      case LC_ENTER_MACRO:
+       reason = "LC_RENAME_MACRO";
+       break;
+      default:
+       reason = "Unknown";
+      }
+      fprintf (stream, "  reason: %d (%s)\n", map->reason, reason);
+
+      const line_map_ordinary *includer_map
+       = linemap_included_from_linemap (line_table, map);
+      fprintf (stream, "  included from location: %d",
+              linemap_included_from (map));
+      if (includer_map) {
+       fprintf (stream, " (in ordinary map %d)",
+                int (includer_map - line_table->info_ordinary.maps));
+      }
+      fprintf (stream, "\n");
 
       /* Render the span of source lines that this "map" covers.  */
-      for (source_location loc = MAP_START_LOCATION (map);
+      for (location_t loc = MAP_START_LOCATION (map);
           loc < end_location;
           loc += (1 << map->m_range_bits) )
        {
@@ -1074,31 +1275,36 @@ dump_location_info (FILE *stream)
          expanded_location exploc
            = linemap_expand_location (line_table, map, loc);
 
-         if (0 == exploc.column)
+         if (exploc.column == 0)
            {
              /* Beginning of a new source line: draw the line.  */
 
-             int line_size;
-             const char *line_text = location_get_source_line (exploc.file,
-                                                               exploc.line,
-                                                               &line_size);
+             char_span line_text = location_get_source_line (exploc.file,
+                                                             exploc.line);
              if (!line_text)
                break;
              fprintf (stream,
                       "%s:%3i|loc:%5i|%.*s\n",
                       exploc.file, exploc.line,
                       loc,
-                      line_size, line_text);
+                      (int)line_text.length (), line_text.get_buffer ());
 
              /* "loc" is at column 0, which means "the whole line".
                 Render the locations *within* the line, by underlining
-                it, showing the source_location numeric values
+                it, showing the location_t numeric values
                 at each column.  */
-             int max_col = (1 << map->m_column_and_range_bits) - 1;
-             if (max_col > line_size)
-               max_col = line_size + 1;
+             size_t max_col = (1 << map->m_column_and_range_bits) - 1;
+             if (max_col > line_text.length ())
+               max_col = line_text.length () + 1;
+
+             int len_lnum = num_digits (exploc.line);
+             if (len_lnum < 3)
+               len_lnum = 3;
+             int len_loc = num_digits (loc);
+             if (len_loc < 5)
+               len_loc = 5;
 
-             int indent = 14 + strlen (exploc.file);
+             int indent = 6 + strlen (exploc.file) + len_lnum + len_loc;
 
              /* Thousands.  */
              if (end_location > 999)
@@ -1126,12 +1332,12 @@ dump_location_info (FILE *stream)
   /* Visualize the macro line_map instances, rendering the sources. */
   for (unsigned int i = 0; i < LINEMAPS_MACRO_USED (line_table); i++)
     {
-      /* Each macro map that is allocated owns source_location values
+      /* Each macro map that is allocated owns location_t values
         that are *lower* that the one before them.
         Hence it's meaningful to view them either in order of ascending
         source locations, or in order of ascending macro map index.  */
-      const bool ascending_source_locations = true;
-      unsigned int idx = (ascending_source_locations
+      const bool ascending_location_ts = true;
+      unsigned int idx = (ascending_location_ts
                          ? (LINEMAPS_MACRO_USED (line_table) - (i + 1))
                          : i);
       const line_map_macro *map = LINEMAPS_MACRO_MAP_AT (line_table, idx);
@@ -1152,8 +1358,8 @@ dump_location_info (FILE *stream)
       fprintf (stream, "  macro_locations:\n");
       for (unsigned int i = 0; i < MACRO_MAP_NUM_MACRO_TOKENS (map); i++)
        {
-         source_location x = MACRO_MAP_LOCATIONS (map)[2 * i];
-         source_location y = MACRO_MAP_LOCATIONS (map)[(2 * i) + 1];
+         location_t x = MACRO_MAP_LOCATIONS (map)[2 * i];
+         location_t y = MACRO_MAP_LOCATIONS (map)[(2 * i) + 1];
 
          /* linemap_add_macro_token encodes token numbers in an expansion
             by putting them after MAP_START_LOCATION. */
@@ -1164,7 +1370,7 @@ dump_location_info (FILE *stream)
             adding 2 extra args for padding tokens; presumably there may
             be a leading and/or trailing padding token injected,
             each for 2 more location slots.
-            This would explain there being up to 4 source_locations slots
+            This would explain there being up to 4 location_ts slots
             that may be uninitialized.  */
 
          fprintf (stream, "    %u: %u, %u\n",
@@ -1174,7 +1380,8 @@ dump_location_info (FILE *stream)
          if (x == y)
            {
              if (x < MAP_START_LOCATION (map))
-               inform (x, "token %u has x-location == y-location == %u", i, x);
+               inform (x, "token %u has %<x-location == y-location == %u%>",
+                       i, x);
              else
                fprintf (stream,
                         "x-location == y-location == %u encodes token # %u\n",
@@ -1182,24 +1389,24 @@ dump_location_info (FILE *stream)
                }
          else
            {
-             inform (x, "token %u has x-location == %u", i, x);
-             inform (x, "token %u has y-location == %u", i, y);
+             inform (x, "token %u has %<x-location == %u%>", i, x);
+             inform (x, "token %u has %<y-location == %u%>", i, y);
            }
        }
       fprintf (stream, "\n");
     }
 
-  /* It appears that MAX_SOURCE_LOCATION itself is never assigned to a
+  /* It appears that MAX_LOCATION_T itself is never assigned to a
      macro map, presumably due to an off-by-one error somewhere
      between the logic in linemap_enter_macro and
      LINEMAPS_MACRO_LOWEST_LOCATION.  */
-  dump_labelled_location_range (stream, "MAX_SOURCE_LOCATION",
-                               MAX_SOURCE_LOCATION,
-                               MAX_SOURCE_LOCATION + 1);
+  dump_labelled_location_range (stream, "MAX_LOCATION_T",
+                               MAX_LOCATION_T,
+                               MAX_LOCATION_T + 1);
 
   /* Visualize ad-hoc values.  */
   dump_labelled_location_range (stream, "AD-HOC LOCATIONS",
-                               MAX_SOURCE_LOCATION + 1, UINT_MAX);
+                               MAX_LOCATION_T + 1, UINT_MAX);
 }
 
 /* string_concat's constructor.  */
@@ -1236,7 +1443,7 @@ string_concat_db::record_string_concatenation (int num, location_t *locs)
   m_table->put (key_loc, concat);
 }
 
-/* Determine if LOC was the location of the the initial token of a
+/* Determine if LOC was the location of the initial token of a
    concatenation of string literal tokens.
    If so, *OUT_NUM is written to with the number of tokens, and
    *OUT_LOCS with the location of an array of locations of the
@@ -1358,24 +1565,32 @@ get_substring_ranges_for_loc (cpp_reader *pfile,
       source_range src_range = get_range_from_loc (line_table, strlocs[i]);
 
       if (src_range.m_start >= LINEMAPS_MACRO_LOWEST_LOCATION (line_table))
-       /* If the string is within a macro expansion, we can't get at the
-          end location.  */
-       return "macro expansion";
-
-      if (src_range.m_start >= LINE_MAP_MAX_LOCATION_WITH_COLS)
-       /* If so, we can't reliably determine where the token started within
-          its line.  */
-       return "range starts after LINE_MAP_MAX_LOCATION_WITH_COLS";
-
-      if (src_range.m_finish >= LINE_MAP_MAX_LOCATION_WITH_COLS)
-       /* If so, we can't reliably determine where the token finished within
-          its line.  */
-       return "range ends after LINE_MAP_MAX_LOCATION_WITH_COLS";
+       {
+         /* If the string token was within a macro expansion, then we can
+            cope with it for the simple case where we have a single token.
+            Otherwise, bail out.  */
+         if (src_range.m_start != src_range.m_finish)
+           return "macro expansion";
+       }
+      else
+       {
+         if (src_range.m_start >= LINE_MAP_MAX_LOCATION_WITH_COLS)
+           /* If so, we can't reliably determine where the token started within
+              its line.  */
+           return "range starts after LINE_MAP_MAX_LOCATION_WITH_COLS";
+
+         if (src_range.m_finish >= LINE_MAP_MAX_LOCATION_WITH_COLS)
+           /* If so, we can't reliably determine where the token finished
+              within its line.  */
+           return "range ends after LINE_MAP_MAX_LOCATION_WITH_COLS";
+       }
 
       expanded_location start
-       = expand_location_to_spelling_point (src_range.m_start);
+       = expand_location_to_spelling_point (src_range.m_start,
+                                            LOCATION_ASPECT_START);
       expanded_location finish
-       = expand_location_to_spelling_point (src_range.m_finish);
+       = expand_location_to_spelling_point (src_range.m_finish,
+                                            LOCATION_ASPECT_FINISH);
       if (start.file != finish.file)
        return "range endpoints are in different files";
       if (start.line != finish.line)
@@ -1383,34 +1598,53 @@ get_substring_ranges_for_loc (cpp_reader *pfile,
       if (start.column > finish.column)
        return "range endpoints are reversed";
 
-      int line_width;
-      const char *line = location_get_source_line (start.file, start.line,
-                                                  &line_width);
-      if (line == NULL)
+      char_span line = location_get_source_line (start.file, start.line);
+      if (!line)
        return "unable to read source line";
 
       /* Determine the location of the literal (including quotes
         and leading prefix chars, such as the 'u' in a u""
         token).  */
-      const char *literal = line + start.column - 1;
-      int literal_length = finish.column - start.column + 1;
+      size_t literal_length = finish.column - start.column + 1;
+
+      /* Ensure that we don't crash if we got the wrong location.  */
+      if (start.column < 1)
+       return "zero start column";
+      if (line.length () < (start.column - 1 + literal_length))
+       return "line is not wide enough";
+
+      char_span literal = line.subspan (start.column - 1, literal_length);
 
-      gcc_assert (line_width >= (start.column - 1 + literal_length));
       cpp_string from;
       from.len = literal_length;
       /* Make a copy of the literal, to avoid having to rely on
         the lifetime of the copy of the line within the cache.
         This will be released by the auto_cpp_string_vec dtor.  */
-      from.text = XDUPVEC (unsigned char, literal, literal_length);
+      from.text = (unsigned char *)literal.xstrdup ();
       strs.safe_push (from);
 
       /* For very long lines, a new linemap could have started
         halfway through the token.
         Ensure that the loc_reader uses the linemap of the
         *end* of the token for its start location.  */
+      const line_map_ordinary *start_ord_map;
+      linemap_resolve_location (line_table, src_range.m_start,
+                               LRK_SPELLING_LOCATION, &start_ord_map);
       const line_map_ordinary *final_ord_map;
       linemap_resolve_location (line_table, src_range.m_finish,
-                               LRK_MACRO_EXPANSION_POINT, &final_ord_map);
+                               LRK_SPELLING_LOCATION, &final_ord_map);
+      if (start_ord_map == NULL || final_ord_map == NULL)
+       return "failed to get ordinary maps";
+      /* Bulletproofing.  We ought to only have different ordinary maps
+        for start vs finish due to line-length jumps.  */
+      if (start_ord_map != final_ord_map
+         && start_ord_map->to_file != final_ord_map->to_file)
+       return "start and finish are spelled in different ordinary maps";
+      /* The file from linemap_resolve_location ought to match that from
+        expand_location_to_spelling_point.  */
+      if (start_ord_map->to_file != start.file)
+       return "mismatching file after resolving linemap";
+
       location_t start_loc
        = linemap_position_for_line_and_column (line_table, final_ord_map,
                                                start.line, start.column);
@@ -1451,12 +1685,12 @@ get_substring_ranges_for_loc (cpp_reader *pfile,
    than for end-users.  */
 
 const char *
-get_source_location_for_substring (cpp_reader *pfile,
-                                  string_concat_db *concats,
-                                  location_t strloc,
-                                  enum cpp_ttype type,
-                                  int caret_idx, int start_idx, int end_idx,
-                                  source_location *out_loc)
+get_location_within_string (cpp_reader *pfile,
+                           string_concat_db *concats,
+                           location_t strloc,
+                           enum cpp_ttype type,
+                           int caret_idx, int start_idx, int end_idx,
+                           location_t *out_loc)
 {
   gcc_checking_assert (caret_idx >= 0);
   gcc_checking_assert (start_idx >= 0);
@@ -1549,6 +1783,21 @@ get_num_source_ranges_for_substring (cpp_reader *pfile,
 
 /* Selftests of location handling.  */
 
+/* Verify that compare() on linenum_type handles comparisons over the full
+   range of the type.  */
+
+static void
+test_linenum_comparisons ()
+{
+  linenum_type min_line (0);
+  linenum_type max_line (0xffffffff);
+  ASSERT_EQ (0, compare (min_line, min_line));
+  ASSERT_EQ (0, compare (max_line, max_line));
+
+  ASSERT_GT (compare (max_line, min_line), 0);
+  ASSERT_LT (compare (min_line, max_line), 0);
+}
+
 /* Helper function for verifying location data: when location_t
    values are > LINE_MAP_MAX_LOCATION_WITH_COLS, they are treated
    as having column 0.  */
@@ -1601,7 +1850,7 @@ assert_loceq (const char *exp_filename, int exp_linenum, int exp_colnum,
    - line_table->default_range_bits: some frontends use a non-zero value
    and others use zero
    - the fallback modes within line-map.c: there are various threshold
-   values for source_location/location_t beyond line-map.c changes
+   values for location_t beyond line-map.c changes
    behavior (disabling of the range-packing optimization, disabling
    of column-tracking).  We can exercise these by starting the line_table
    at interesting values at or near these thresholds.
@@ -1609,8 +1858,9 @@ assert_loceq (const char *exp_filename, int exp_linenum, int exp_colnum,
    The following struct describes a particular case within our test
    matrix.  */
 
-struct line_table_case
+class line_table_case
 {
+public:
   line_table_case (int default_range_bits, int base_location)
   : m_default_range_bits (default_range_bits),
     m_base_location (base_location)
@@ -1847,24 +2097,23 @@ test_reading_source_line ()
                        "This is the 3rd line");
 
   /* Read back a specific line from the tempfile.  */
-  int line_size;
-  const char *source_line = location_get_source_line (tmp.get_filename (),
-                                                     3, &line_size);
-  ASSERT_TRUE (source_line != NULL);
-  ASSERT_EQ (20, line_size);
+  char_span source_line = location_get_source_line (tmp.get_filename (), 3);
+  ASSERT_TRUE (source_line);
+  ASSERT_TRUE (source_line.get_buffer () != NULL);
+  ASSERT_EQ (20, source_line.length ());
   ASSERT_TRUE (!strncmp ("This is the 3rd line",
-                        source_line, line_size));
+                        source_line.get_buffer (), source_line.length ()));
 
-  source_line = location_get_source_line (tmp.get_filename (),
-                                         2, &line_size);
-  ASSERT_TRUE (source_line != NULL);
-  ASSERT_EQ (21, line_size);
+  source_line = location_get_source_line (tmp.get_filename (), 2);
+  ASSERT_TRUE (source_line);
+  ASSERT_TRUE (source_line.get_buffer () != NULL);
+  ASSERT_EQ (21, source_line.length ());
   ASSERT_TRUE (!strncmp ("This is the test text",
-                        source_line, line_size));
+                        source_line.get_buffer (), source_line.length ()));
 
-  source_line = location_get_source_line (tmp.get_filename (),
-                                         4, &line_size);
-  ASSERT_TRUE (source_line == NULL);
+  source_line = location_get_source_line (tmp.get_filename (), 4);
+  ASSERT_FALSE (source_line);
+  ASSERT_TRUE (source_line.get_buffer () == NULL);
 }
 
 /* Tests of lexing.  */
@@ -1967,7 +2216,7 @@ test_lexer (const line_table_case &case_)
 
 /* Forward decls.  */
 
-struct lexer_test;
+class lexer_test;
 class lexer_test_options;
 
 /* A class for specifying options of a lexer_test.
@@ -2004,8 +2253,9 @@ class cpp_reader_ptr
 
 /* A struct for writing lexer tests.  */
 
-struct lexer_test
+class lexer_test
 {
+public:
   lexer_test (const line_table_case &case_, const char *content,
              lexer_test_options *options);
   ~lexer_test ();
@@ -2053,14 +2303,14 @@ class ebcdic_execution_charset : public lexer_test_options
     cpp_opts->narrow_charset = "IBM1047";
 
     cpp_callbacks *callbacks = cpp_get_callbacks (test.m_parser);
-    callbacks->error = on_error;
+    callbacks->diagnostic = on_diagnostic;
   }
 
-  static bool on_error (cpp_reader *pfile ATTRIBUTE_UNUSED,
-                       int level ATTRIBUTE_UNUSED,
-                       int reason ATTRIBUTE_UNUSED,
-                       rich_location *richloc ATTRIBUTE_UNUSED,
-                       const char *msgid, va_list *ap ATTRIBUTE_UNUSED)
+  static bool on_diagnostic (cpp_reader *pfile ATTRIBUTE_UNUSED,
+                            enum cpp_diagnostic_level level ATTRIBUTE_UNUSED,
+                            enum cpp_warning_reason reason ATTRIBUTE_UNUSED,
+                            rich_location *richloc ATTRIBUTE_UNUSED,
+                            const char *msgid, va_list *ap ATTRIBUTE_UNUSED)
     ATTRIBUTE_FPTR_PRINTF(5,0)
   {
     gcc_assert (s_singleton);
@@ -2090,53 +2340,53 @@ class ebcdic_execution_charset : public lexer_test_options
 
 ebcdic_execution_charset *ebcdic_execution_charset::s_singleton;
 
-/* A lexer_test_options subclass that records a list of error
+/* A lexer_test_options subclass that records a list of diagnostic
    messages emitted by the lexer.  */
 
-class lexer_error_sink : public lexer_test_options
+class lexer_diagnostic_sink : public lexer_test_options
 {
  public:
-  lexer_error_sink ()
+  lexer_diagnostic_sink ()
   {
     gcc_assert (s_singleton == NULL);
     s_singleton = this;
   }
-  ~lexer_error_sink ()
+  ~lexer_diagnostic_sink ()
   {
     gcc_assert (s_singleton == this);
     s_singleton = NULL;
 
     int i;
     char *str;
-    FOR_EACH_VEC_ELT (m_errors, i, str)
+    FOR_EACH_VEC_ELT (m_diagnostics, i, str)
       free (str);
   }
 
   void apply (lexer_test &test) FINAL OVERRIDE
   {
     cpp_callbacks *callbacks = cpp_get_callbacks (test.m_parser);
-    callbacks->error = on_error;
+    callbacks->diagnostic = on_diagnostic;
   }
 
-  static bool on_error (cpp_reader *pfile ATTRIBUTE_UNUSED,
-                       int level ATTRIBUTE_UNUSED,
-                       int reason ATTRIBUTE_UNUSED,
-                       rich_location *richloc ATTRIBUTE_UNUSED,
-                       const char *msgid, va_list *ap)
+  static bool on_diagnostic (cpp_reader *pfile ATTRIBUTE_UNUSED,
+                            enum cpp_diagnostic_level level ATTRIBUTE_UNUSED,
+                            enum cpp_warning_reason reason ATTRIBUTE_UNUSED,
+                            rich_location *richloc ATTRIBUTE_UNUSED,
+                            const char *msgid, va_list *ap)
     ATTRIBUTE_FPTR_PRINTF(5,0)
   {
     char *msg = xvasprintf (msgid, *ap);
-    s_singleton->m_errors.safe_push (msg);
+    s_singleton->m_diagnostics.safe_push (msg);
     return true;
   }
 
-  auto_vec<char *> m_errors;
+  auto_vec<char *> m_diagnostics;
 
  private:
-  static lexer_error_sink *s_singleton;
+  static lexer_diagnostic_sink *s_singleton;
 };
 
-lexer_error_sink *lexer_error_sink::s_singleton;
+lexer_diagnostic_sink *lexer_diagnostic_sink::s_singleton;
 
 /* Constructor.  Override line_table with a new instance based on CASE_,
    and write CONTENT to a tempfile.  Create a cpp_reader, and use it to
@@ -2600,7 +2850,7 @@ test_lexer_string_locations_ucn4 (const line_table_case &case_)
 
   /* Verify that cpp_interpret_string works.
      The string should be encoded in the execution character
-     set.  Assuming that that is UTF-8, we should have the following:
+     set.  Assuming that is UTF-8, we should have the following:
      -----------  ----  -----  -------  ----------------
      Byte offset  Byte  Octal  Unicode  Source Column(s)
      -----------  ----  -----  -------  ----------------
@@ -3350,21 +3600,21 @@ test_lexer_string_locations_raw_string_unterminated (const line_table_case &case
 {
   const char *content = "R\"ouch()ouCh\" /* etc */";
 
-  lexer_error_sink errors;
-  lexer_test test (case_, content, &errors);
+  lexer_diagnostic_sink diagnostics;
+  lexer_test test (case_, content, &diagnostics);
   test.m_implicitly_expect_EOF = false;
 
   /* Attempt to parse the raw string.  */
   const cpp_token *tok = test.get_token ();
   ASSERT_EQ (tok->type, CPP_EOF);
 
-  ASSERT_EQ (1, errors.m_errors.length ());
+  ASSERT_EQ (1, diagnostics.m_diagnostics.length ());
   /* We expect the message "unterminated raw string"
      in the "cpplib" translation domain.
      It's not clear that dgettext is available on all supported hosts,
      so this assertion is commented-out for now.
        ASSERT_STREQ (dgettext ("cpplib", "unterminated raw string"),
-                     errors.m_errors[0]);
+                     diagnostics.m_diagnostics[0]);
   */
 }
 
@@ -3477,11 +3727,147 @@ for_each_line_table_case (void (*testcase) (const line_table_case &))
   ASSERT_EQ (num_cases_tested, 2 * 12);
 }
 
+/* Verify that when presented with a consecutive pair of locations with
+   a very large line offset, we don't attempt to consolidate them into
+   a single ordinary linemap where the line offsets within the line map
+   would lead to overflow (PR lto/88147).  */
+
+static void
+test_line_offset_overflow ()
+{
+  line_table_test ltt (line_table_case (5, 0));
+
+  linemap_add (line_table, LC_ENTER, false, "foo.c", 0);
+  linemap_line_start (line_table, 1, 100);
+  location_t loc_a = linemap_line_start (line_table, 2578, 255);
+  assert_loceq ("foo.c", 2578, 0, loc_a);
+
+  const line_map_ordinary *ordmap_a = LINEMAPS_LAST_ORDINARY_MAP (line_table);
+  ASSERT_EQ (ordmap_a->m_column_and_range_bits, 13);
+  ASSERT_EQ (ordmap_a->m_range_bits, 5);
+
+  location_t loc_b = linemap_line_start (line_table, 404198, 512);
+  assert_loceq ("foo.c", 404198, 0, loc_b);
+
+  /* We should have started a new linemap, rather than attempting to store
+     a very large line offset.  */
+  const line_map_ordinary *ordmap_b = LINEMAPS_LAST_ORDINARY_MAP (line_table);
+  ASSERT_NE (ordmap_a, ordmap_b);
+}
+
+void test_cpp_utf8 ()
+{
+  const int def_tabstop = 8;
+  /* Verify that wcwidth of invalid UTF-8 or control bytes is 1.  */
+  {
+    int w_bad = cpp_display_width ("\xf0!\x9f!\x98!\x82!", 8, def_tabstop);
+    ASSERT_EQ (8, w_bad);
+    int w_ctrl = cpp_display_width ("\r\n\v\0\1", 5, def_tabstop);
+    ASSERT_EQ (5, w_ctrl);
+  }
+
+  /* Verify that wcwidth of valid UTF-8 is as expected.  */
+  {
+    const int w_pi = cpp_display_width ("\xcf\x80", 2, def_tabstop);
+    ASSERT_EQ (1, w_pi);
+    const int w_emoji = cpp_display_width ("\xf0\x9f\x98\x82", 4, def_tabstop);
+    ASSERT_EQ (2, w_emoji);
+    const int w_umlaut_precomposed = cpp_display_width ("\xc3\xbf", 2,
+                                                       def_tabstop);
+    ASSERT_EQ (1, w_umlaut_precomposed);
+    const int w_umlaut_combining = cpp_display_width ("y\xcc\x88", 3,
+                                                     def_tabstop);
+    ASSERT_EQ (1, w_umlaut_combining);
+    const int w_han = cpp_display_width ("\xe4\xb8\xba", 3, def_tabstop);
+    ASSERT_EQ (2, w_han);
+    const int w_ascii = cpp_display_width ("GCC", 3, def_tabstop);
+    ASSERT_EQ (3, w_ascii);
+    const int w_mixed = cpp_display_width ("\xcf\x80 = 3.14 \xf0\x9f\x98\x82"
+                                          "\x9f! \xe4\xb8\xba y\xcc\x88",
+                                          24, def_tabstop);
+    ASSERT_EQ (18, w_mixed);
+  }
+
+  /* Verify that display width properly expands tabs.  */
+  {
+    const char *tstr = "\tabc\td";
+    ASSERT_EQ (6, cpp_display_width (tstr, 6, 1));
+    ASSERT_EQ (10, cpp_display_width (tstr, 6, 3));
+    ASSERT_EQ (17, cpp_display_width (tstr, 6, 8));
+    ASSERT_EQ (1, cpp_display_column_to_byte_column (tstr, 6, 7, 8));
+  }
+
+  /* Verify that cpp_byte_column_to_display_column can go past the end,
+     and similar edge cases.  */
+  {
+    const char *str
+      /* Display columns.
+         111111112345  */
+      = "\xcf\x80 abc";
+      /* 111122223456
+        Byte columns.  */
+
+    ASSERT_EQ (5, cpp_display_width (str, 6, def_tabstop));
+    ASSERT_EQ (105,
+              cpp_byte_column_to_display_column (str, 6, 106, def_tabstop));
+    ASSERT_EQ (10000,
+              cpp_byte_column_to_display_column (NULL, 0, 10000, def_tabstop));
+    ASSERT_EQ (0,
+              cpp_byte_column_to_display_column (NULL, 10000, 0, def_tabstop));
+  }
+
+  /* Verify that cpp_display_column_to_byte_column can go past the end,
+     and similar edge cases, and check invertibility.  */
+  {
+    const char *str
+      /* Display columns.
+        000000000000000000000000000000000000011
+        111111112222222234444444455555555678901  */
+      = "\xf0\x9f\x98\x82 \xf0\x9f\x98\x82 hello";
+      /* 000000000000000000000000000000000111111
+        111122223333444456666777788889999012345
+        Byte columns.  */
+    ASSERT_EQ (4, cpp_display_column_to_byte_column (str, 15, 2, def_tabstop));
+    ASSERT_EQ (15,
+              cpp_display_column_to_byte_column (str, 15, 11, def_tabstop));
+    ASSERT_EQ (115,
+              cpp_display_column_to_byte_column (str, 15, 111, def_tabstop));
+    ASSERT_EQ (10000,
+              cpp_display_column_to_byte_column (NULL, 0, 10000, def_tabstop));
+    ASSERT_EQ (0,
+              cpp_display_column_to_byte_column (NULL, 10000, 0, def_tabstop));
+
+    /* Verify that we do not interrupt a UTF-8 sequence.  */
+    ASSERT_EQ (4, cpp_display_column_to_byte_column (str, 15, 1, def_tabstop));
+
+    for (int byte_col = 1; byte_col <= 15; ++byte_col)
+      {
+       const int disp_col
+         = cpp_byte_column_to_display_column (str, 15, byte_col, def_tabstop);
+       const int byte_col2
+         = cpp_display_column_to_byte_column (str, 15, disp_col, def_tabstop);
+
+       /* If we ask for the display column in the middle of a UTF-8
+          sequence, it will return the length of the partial sequence,
+          matching the behavior of GCC before display column support.
+          Otherwise check the round trip was successful.  */
+       if (byte_col < 4)
+         ASSERT_EQ (byte_col, disp_col);
+       else if (byte_col >= 6 && byte_col < 9)
+         ASSERT_EQ (3 + (byte_col - 5), disp_col);
+       else
+         ASSERT_EQ (byte_col2, byte_col);
+      }
+  }
+
+}
+
 /* Run all of the selftests within this file.  */
 
 void
 input_c_tests ()
 {
+  test_linenum_comparisons ();
   test_should_have_column_data_p ();
   test_unknown_location ();
   test_builtins ();
@@ -3515,6 +3901,10 @@ input_c_tests ()
   for_each_line_table_case (test_lexer_char_constants);
 
   test_reading_source_line ();
+
+  test_line_offset_overflow ();
+
+  test_cpp_utf8 ();
 }
 
 } // namespace selftest