Fix ICE for missing header fix-it hints with overlarge #line directives (PR c/84852)
authorDavid Malcolm <dmalcolm@redhat.com>
Wed, 14 Mar 2018 13:58:13 +0000 (13:58 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Wed, 14 Mar 2018 13:58:13 +0000 (13:58 +0000)
PR c/84852 reports an ICE inside diagnostic_show_locus when printing
a diagnostic for a source file with a #line >= 2^31:

  #line 7777777777
  int foo (void) { return strlen(""); }

where we're attempting to print a fix-it hint at the top of the file
and underline the "strlen" (two "line spans").

The
  #line 7777777777
won't fix within the 32-bit linenum_type, and is truncated from
  0x1cf977871
to
   0xcf977871
i.e. 3482810481 in decimal.

Such a #line is reported by -pedantic and -pedantic-errors, but we
shouldn't ICE.

The ICE is an assertion failure within layout::calculate_line_spans,
where the line spans have not been properly sorted.

The layout_ranges are stored as int, rather than linenum_type,
giving line -812156815 for the error, and line 1 for the fix-it hint.

However, line_span uses linenum_type rather than int.

line_span::comparator compares these values as int, and hence
decides that (linenum_type)3482810481 aka (int)-812156815 is less
than line 1.

This leads to this assertion failing in layout::calculate_line_spans:

1105       gcc_assert (next->m_first_line >= current->m_first_line);

since it isn't the case that 1 >= 3482810481.

The underlying problem is the mix of types for storing line numbers:
in parts of libcpp and diagnostic-show-locus.c we use linenum_type;
in other places (including libcpp's expanded_location) we use int.

I looked at using linenum_type throughout, but doing so turned into
a large patch, so this patch fixes the ICE in a less invasive way
by merely using linenum_type more consistently just within
diagnostic-show-locus.c, and fixing line_span::comparator to properly
handle line numbers (and line number differences) >= 2^31, by using
a new helper function for linenum_type differences, computing the
difference using long long, and using the sign of the difference
(as the difference might not fit in the "int" return type imposed
by qsort).

gcc/ChangeLog:
PR c/84852
* diagnostic-show-locus.c (class layout_point): Convert m_line
from int to linenum_type.
(line_span::comparator): Use linenum "compare" function when
comparing line numbers.
(test_line_span): New function.
(layout_range::contains_point): Convert param "row" from int to
linenum_type.
(layout_range::intersects_line_p): Likewise.
(layout::will_show_line_p): Likewise.
(layout::print_source_line): Likewise.
(layout::should_print_annotation_line_p): Likewise.
(layout::print_annotation_line): Likewise.
(layout::print_leading_fixits): Likewise.
(layout::annotation_line_showed_range_p): Likewise.
(struct line_corrections): Likewise for field m_row.
(line_corrections::line_corrections): Likewise for param "row".
(layout::print_trailing_fixits): Likewise.
(layout::get_state_at_point): Likewise.
(layout::get_x_bound_for_row): Likewise.
(layout::print_line): Likewise.
(diagnostic_show_locus): Likewise for locals "last_line" and
"row".
(selftest::diagnostic_show_locus_c_tests): Call test_line_span.
* input.c (selftest::test_linenum_comparisons): New function.
(selftest::input_c_tests): Call it.
* selftest.c (selftest::test_assertions): Test ASSERT_GT,
ASSERT_GT_AT, ASSERT_LT, and ASSERT_LT_AT.
* selftest.h (ASSERT_GT): New macro.
(ASSERT_GT_AT): New macro.
(ASSERT_LT): New macro.
(ASSERT_LT_AT): New macro.

gcc/testsuite/ChangeLog:
PR c/84852
* gcc.dg/fixits-pr84852-1.c: New test.
* gcc.dg/fixits-pr84852-2.c: New test.

libcpp/ChangeLog:
* include/line-map.h (compare): New function on linenum_type.

From-SVN: r258526

gcc/ChangeLog
gcc/diagnostic-show-locus.c
gcc/input.c
gcc/selftest.c
gcc/selftest.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/fixits-pr84852-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fixits-pr84852-2.c [new file with mode: 0644]
libcpp/ChangeLog
libcpp/include/line-map.h

index fcd11eb7db37ae012319343d19605b20aab07afc..8f81cc392aa101f824a69d9b8bc9a9bd4ce27df3 100644 (file)
@@ -1,3 +1,38 @@
+2018-03-14  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c/84852
+       * diagnostic-show-locus.c (class layout_point): Convert m_line
+       from int to linenum_type.
+       (line_span::comparator): Use linenum "compare" function when
+       comparing line numbers.
+       (test_line_span): New function.
+       (layout_range::contains_point): Convert param "row" from int to
+       linenum_type.
+       (layout_range::intersects_line_p): Likewise.
+       (layout::will_show_line_p): Likewise.
+       (layout::print_source_line): Likewise.
+       (layout::should_print_annotation_line_p): Likewise.
+       (layout::print_annotation_line): Likewise.
+       (layout::print_leading_fixits): Likewise.
+       (layout::annotation_line_showed_range_p): Likewise.
+       (struct line_corrections): Likewise for field m_row.
+       (line_corrections::line_corrections): Likewise for param "row".
+       (layout::print_trailing_fixits): Likewise.
+       (layout::get_state_at_point): Likewise.
+       (layout::get_x_bound_for_row): Likewise.
+       (layout::print_line): Likewise.
+       (diagnostic_show_locus): Likewise for locals "last_line" and
+       "row".
+       (selftest::diagnostic_show_locus_c_tests): Call test_line_span.
+       * input.c (selftest::test_linenum_comparisons): New function.
+       (selftest::input_c_tests): Call it.
+       * selftest.c (selftest::test_assertions): Test ASSERT_GT,
+       ASSERT_GT_AT, ASSERT_LT, and ASSERT_LT_AT.
+       * selftest.h (ASSERT_GT): New macro.
+       (ASSERT_GT_AT): New macro.
+       (ASSERT_LT): New macro.
+       (ASSERT_LT_AT): New macro.
+
 2018-03-14  Segher Boessenkool  <segher@kernel.crashing.org>
 
        PR rtl-optimization/84780
index 5eee3cc144991339db35a0670f4ab4b3808936eb..bdf608a08bcad6e308549dea23dabdc46a467ef0 100644 (file)
@@ -115,7 +115,7 @@ class layout_point
   : m_line (exploc.line),
     m_column (exploc.column) {}
 
-  int m_line;
+  linenum_type m_line;
   int m_column;
 };
 
@@ -129,8 +129,8 @@ class layout_range
                bool show_caret_p,
                const expanded_location *caret_exploc);
 
-  bool contains_point (int row, int column) const;
-  bool intersects_line_p (int row) const;
+  bool contains_point (linenum_type row, int column) const;
+  bool intersects_line_p (linenum_type row) const;
 
   layout_point m_start;
   layout_point m_finish;
@@ -172,16 +172,52 @@ struct line_span
   {
     const line_span *ls1 = (const line_span *)p1;
     const line_span *ls2 = (const line_span *)p2;
-    int first_line_diff = (int)ls1->m_first_line - (int)ls2->m_first_line;
-    if (first_line_diff)
-      return first_line_diff;
-    return (int)ls1->m_last_line - (int)ls2->m_last_line;
+    int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
+    if (first_line_cmp)
+      return first_line_cmp;
+    return compare (ls1->m_last_line, ls2->m_last_line);
   }
 
   linenum_type m_first_line;
   linenum_type m_last_line;
 };
 
+#if CHECKING_P
+
+/* Selftests for line_span.  */
+
+static void
+test_line_span ()
+{
+  line_span line_one (1, 1);
+  ASSERT_EQ (1, line_one.get_first_line ());
+  ASSERT_EQ (1, line_one.get_last_line ());
+  ASSERT_FALSE (line_one.contains_line_p (0));
+  ASSERT_TRUE (line_one.contains_line_p (1));
+  ASSERT_FALSE (line_one.contains_line_p (2));
+
+  line_span lines_1_to_3 (1, 3);
+  ASSERT_EQ (1, lines_1_to_3.get_first_line ());
+  ASSERT_EQ (3, lines_1_to_3.get_last_line ());
+  ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
+  ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
+
+  ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
+  ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
+  ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
+
+  /* A linenum > 2^31.  */
+  const linenum_type LARGEST_LINE = 0xffffffff;
+  line_span largest_line (LARGEST_LINE, LARGEST_LINE);
+  ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
+  ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
+
+  ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
+  ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
+}
+
+#endif /* #if CHECKING_P */
+
 /* A class to control the overall layout when printing a diagnostic.
 
    The layout is determined within the constructor.
@@ -207,18 +243,18 @@ class layout
 
   expanded_location get_expanded_location (const line_span *) const;
 
-  void print_line (int row);
+  void print_line (linenum_type row);
 
  private:
-  bool will_show_line_p (int row) const;
-  void print_leading_fixits (int row);
-  void print_source_line (int row, const char *line, int line_width,
+  bool will_show_line_p (linenum_type row) const;
+  void print_leading_fixits (linenum_type row);
+  void print_source_line (linenum_type row, const char *line, int line_width,
                          line_bounds *lbounds_out);
-  bool should_print_annotation_line_p (int row) const;
-  void print_annotation_line (int row, const line_bounds lbounds);
-  void print_trailing_fixits (int row);
+  bool should_print_annotation_line_p (linenum_type row) const;
+  void print_annotation_line (linenum_type row, const line_bounds lbounds);
+  void print_trailing_fixits (linenum_type row);
 
-  bool annotation_line_showed_range_p (int line, int start_column,
+  bool annotation_line_showed_range_p (linenum_type line, int start_column,
                                       int finish_column) const;
   void show_ruler (int max_column) const;
 
@@ -230,13 +266,13 @@ class layout
 
   bool
   get_state_at_point (/* Inputs.  */
-                     int row, int column,
+                     linenum_type row, int column,
                      int first_non_ws, int last_non_ws,
                      /* Outputs.  */
                      point_state *out_state);
 
   int
-  get_x_bound_for_row (int row, int caret_column,
+  get_x_bound_for_row (linenum_type row, int caret_column,
                       int last_non_ws);
 
   void
@@ -417,7 +453,7 @@ layout_range::layout_range (const expanded_location *start_exploc,
    - 'a' indicates a subsequent point *after* the range.  */
 
 bool
-layout_range::contains_point (int row, int column) const
+layout_range::contains_point (linenum_type row, int column) const
 {
   gcc_assert (m_start.m_line <= m_finish.m_line);
   /* ...but the equivalent isn't true for the columns;
@@ -478,7 +514,7 @@ layout_range::contains_point (int row, int column) const
 /* Does this layout_range contain any part of line ROW?  */
 
 bool
-layout_range::intersects_line_p (int row) const
+layout_range::intersects_line_p (linenum_type row) const
 {
   gcc_assert (m_start.m_line <= m_finish.m_line);
   if (row < m_start.m_line)
@@ -940,7 +976,7 @@ layout::maybe_add_location_range (const location_range *loc_range,
 /* Return true iff ROW is within one of the line spans for this layout.  */
 
 bool
-layout::will_show_line_p (int row) const
+layout::will_show_line_p (linenum_type row) const
 {
   for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
        line_span_idx++)
@@ -1138,7 +1174,7 @@ layout::calculate_line_spans ()
    is its width.  */
 
 void
-layout::print_source_line (int row, const char *line, int line_width,
+layout::print_source_line (linenum_type row, const char *line, int line_width,
                           line_bounds *lbounds_out)
 {
   m_colorizer.set_normal_text ();
@@ -1201,7 +1237,7 @@ layout::print_source_line (int row, const char *line, int line_width,
    i.e. if any of m_layout_ranges contains ROW.  */
 
 bool
-layout::should_print_annotation_line_p (int row) const
+layout::should_print_annotation_line_p (linenum_type row) const
 {
   layout_range *range;
   int i;
@@ -1215,7 +1251,7 @@ layout::should_print_annotation_line_p (int row) const
    source line.  */
 
 void
-layout::print_annotation_line (int row, const line_bounds lbounds)
+layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
 {
   int x_bound = get_x_bound_for_row (row, m_exploc.column,
                                     lbounds.m_last_non_ws);
@@ -1263,7 +1299,7 @@ layout::print_annotation_line (int row, const line_bounds lbounds)
    itself, with a leading '+'.  */
 
 void
-layout::print_leading_fixits (int row)
+layout::print_leading_fixits (linenum_type row)
 {
   for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
     {
@@ -1301,7 +1337,7 @@ layout::print_leading_fixits (int row)
    the exact range from START_COLUMN to FINISH_COLUMN.  */
 
 bool
-layout::annotation_line_showed_range_p (int line, int start_column,
+layout::annotation_line_showed_range_p (linenum_type line, int start_column,
                                        int finish_column) const
 {
   layout_range *range;
@@ -1552,7 +1588,7 @@ correction::ensure_terminated ()
 
 struct line_corrections
 {
-  line_corrections (const char *filename, int row)
+  line_corrections (const char *filename, linenum_type row)
   : m_filename (filename), m_row (row)
   {}
   ~line_corrections ();
@@ -1560,7 +1596,7 @@ struct line_corrections
   void add_hint (const fixit_hint *hint);
 
   const char *m_filename;
-  int m_row;
+  linenum_type m_row;
   auto_vec <correction *> m_corrections;
 };
 
@@ -1674,7 +1710,7 @@ line_corrections::add_hint (const fixit_hint *hint)
    in layout::print_leading_fixits.  */
 
 void
-layout::print_trailing_fixits (int row)
+layout::print_trailing_fixits (linenum_type row)
 {
   /* Build a list of correction instances for the line,
      potentially consolidating hints (for the sake of readability).  */
@@ -1761,7 +1797,7 @@ layout::print_newline ()
 
 bool
 layout::get_state_at_point (/* Inputs.  */
-                           int row, int column,
+                           linenum_type row, int column,
                            int first_non_ws, int last_non_ws,
                            /* Outputs.  */
                            point_state *out_state)
@@ -1806,7 +1842,7 @@ layout::get_state_at_point (/* Inputs.  */
    character of source (as determined when printing the source line).  */
 
 int
-layout::get_x_bound_for_row (int row, int caret_column,
+layout::get_x_bound_for_row (linenum_type row, int caret_column,
                             int last_non_ws_column)
 {
   int result = caret_column + 1;
@@ -1897,7 +1933,7 @@ layout::show_ruler (int max_column) const
    consisting of any caret/underlines, then any fixits.
    If the source line can't be read, print nothing.  */
 void
-layout::print_line (int row)
+layout::print_line (linenum_type row)
 {
   int line_width;
   const char *line = location_get_source_line (m_exploc.file, row,
@@ -1977,8 +2013,9 @@ diagnostic_show_locus (diagnostic_context * context,
          expanded_location exploc = layout.get_expanded_location (line_span);
          context->start_span (context, exploc);
        }
-      int last_line = line_span->get_last_line ();
-      for (int row = line_span->get_first_line (); row <= last_line; row++)
+      linenum_type last_line = line_span->get_last_line ();
+      for (linenum_type row = line_span->get_first_line ();
+          row <= last_line; row++)
        layout.print_line (row);
     }
 
@@ -3129,6 +3166,8 @@ test_fixit_deletion_affecting_newline (const line_table_case &case_)
 void
 diagnostic_show_locus_c_tests ()
 {
+  test_line_span ();
+
   test_layout_range_for_single_point ();
   test_layout_range_for_single_line ();
   test_layout_range_for_multiple_lines ();
index 081e7856916d823c597344f1ca91621c0fb33959..b6675768722f606320faffea3c5f3eed34bfaa2b 100644 (file)
@@ -1595,6 +1595,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.  */
@@ -3528,6 +3543,7 @@ for_each_line_table_case (void (*testcase) (const line_table_case &))
 void
 input_c_tests ()
 {
+  test_linenum_comparisons ();
   test_should_have_column_data_p ();
   test_unknown_location ();
   test_builtins ();
index 0f6c5e4f38c7e7379d915e757d0479582b067880..5709110c291d04e618f74fa18015cddbdf7729e4 100644 (file)
@@ -288,6 +288,10 @@ test_assertions ()
   ASSERT_EQ (1, 1);
   ASSERT_EQ_AT (SELFTEST_LOCATION, 1, 1);
   ASSERT_NE (1, 2);
+  ASSERT_GT (2, 1);
+  ASSERT_GT_AT (SELFTEST_LOCATION, 2, 1);
+  ASSERT_LT (1, 2);
+  ASSERT_LT_AT (SELFTEST_LOCATION, 1, 2);
   ASSERT_STREQ ("test", "test");
   ASSERT_STREQ_AT (SELFTEST_LOCATION, "test", "test");
   ASSERT_STR_CONTAINS ("foo bar baz", "bar");
index 67d55ebe13032025fb22f2a47d3d7aa2c4f64c51..e3117c6bfc425c124b26bdd9f2b92b7db246952b 100644 (file)
@@ -333,6 +333,44 @@ extern int num_passes;
     ::selftest::fail ((LOC), desc);                                    \
   SELFTEST_END_STMT
 
+/* Evaluate LHS and RHS and compare them with >, calling
+   ::selftest::pass if LHS > RHS,
+   ::selftest::fail otherwise.  */
+
+#define ASSERT_GT(LHS, RHS)                            \
+  ASSERT_GT_AT ((SELFTEST_LOCATION), (LHS), (RHS))
+
+/* Like ASSERT_GT, but treat LOC as the effective location of the
+   selftest.  */
+
+#define ASSERT_GT_AT(LOC, LHS, RHS)                   \
+  SELFTEST_BEGIN_STMT                                         \
+  const char *desc_ = "ASSERT_GT (" #LHS ", " #RHS ")";               \
+  if ((LHS) > (RHS))                                          \
+    ::selftest::pass ((LOC), desc_);                          \
+  else                                                        \
+    ::selftest::fail ((LOC), desc_);                          \
+  SELFTEST_END_STMT
+
+/* Evaluate LHS and RHS and compare them with <, calling
+   ::selftest::pass if LHS < RHS,
+   ::selftest::fail otherwise.  */
+
+#define ASSERT_LT(LHS, RHS)                            \
+  ASSERT_LT_AT ((SELFTEST_LOCATION), (LHS), (RHS))
+
+/* Like ASSERT_LT, but treat LOC as the effective location of the
+   selftest.  */
+
+#define ASSERT_LT_AT(LOC, LHS, RHS)                   \
+  SELFTEST_BEGIN_STMT                                         \
+  const char *desc_ = "ASSERT_LT (" #LHS ", " #RHS ")";               \
+  if ((LHS) < (RHS))                                          \
+    ::selftest::pass ((LOC), desc_);                          \
+  else                                                        \
+    ::selftest::fail ((LOC), desc_);                          \
+  SELFTEST_END_STMT
+
 /* Evaluate EXPECTED and ACTUAL and compare them with strcmp, calling
    ::selftest::pass if they are equal,
    ::selftest::fail if they are non-equal.  */
index f89a29a9c98fff21f45172c78b221b706f619949..14b8e99a04f5cfaaf897a247a3ee3a6639d02637 100644 (file)
@@ -1,3 +1,9 @@
+2018-03-14  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c/84852
+       * gcc.dg/fixits-pr84852-1.c: New test.
+       * gcc.dg/fixits-pr84852-2.c: New test.
+
 2018-03-14  Thomas Preud'homme  <thomas.preudhomme@arm.com>
 
        * lib/scanasm.exp (scan-assembler-times): Move FAIL debug info into a
diff --git a/gcc/testsuite/gcc.dg/fixits-pr84852-1.c b/gcc/testsuite/gcc.dg/fixits-pr84852-1.c
new file mode 100644 (file)
index 0000000..ed88434
--- /dev/null
@@ -0,0 +1,25 @@
+/* This is padding (to avoid the output containing DejaGnu directives).  */
+
+/* We need -fdiagnostics-show-caret to trigger the ICE.  */
+
+/* { dg-options "-fdiagnostics-show-caret -pedantic-errors -Wno-implicit-function-declaration" } */
+
+#line 3482810481 /* { dg-error "line number out of range" } */
+/* { dg-begin-multiline-output "" }
+ #line 3482810481
+       ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+int foo (void) { return strlen(""); }
+
+/* { dg-warning "incompatible implicit declaration of built-in function 'strlen'" "" { target *-*-* } -812156810 } */
+/* { dg-message "include '<string.h>' or provide a declaration of 'strlen'" "" { target *-*-* } -812156810 } */
+#if 0
+{ dg-begin-multiline-output "" }
++#include <string.h>
+ /* This is padding (to avoid the output containing DejaGnu directives).  */
+{ dg-end-multiline-output "" }
+#endif
+
+/* We need this, to consume a stray line marker for the bogus line.  */
+/* { dg-regexp ".*fixits-pr84852.c:-812156810:25:" } */
diff --git a/gcc/testsuite/gcc.dg/fixits-pr84852-2.c b/gcc/testsuite/gcc.dg/fixits-pr84852-2.c
new file mode 100644 (file)
index 0000000..0674ef5
--- /dev/null
@@ -0,0 +1,25 @@
+/* This is padding (to avoid the output containing DejaGnu directives).  */
+
+/* We need -fdiagnostics-show-caret to trigger the ICE.  */
+
+/* { dg-options "-fdiagnostics-show-caret -pedantic-errors -Wno-implicit-function-declaration" } */
+
+#line 7777777777 /* { dg-error "line number out of range" } */
+/* { dg-begin-multiline-output "" }
+ #line 7777777777
+       ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+int foo (void) { return strlen(""); }
+
+/* { dg-warning "incompatible implicit declaration of built-in function 'strlen'" "" { target *-*-* } -812156810 } */
+/* { dg-message "include '<string.h>' or provide a declaration of 'strlen'" "" { target *-*-* } -812156810 } */
+#if 0
+{ dg-begin-multiline-output "" }
++#include <string.h>
+ /* This is padding (to avoid the output containing DejaGnu directives).  */
+{ dg-end-multiline-output "" }
+#endif
+
+/* We need this, to consume a stray line marker for the bogus line.  */
+/* { dg-regexp ".*fixits-pr84852-2.c:-812156810:25:" } */
index 791c364f01b2017c26c932b0805c99be80f269d9..a66948ff1ace0388f8c0afb018446301a792c5ce 100644 (file)
@@ -1,3 +1,7 @@
+2018-03-14  David Malcolm  <dmalcolm@redhat.com>
+
+       * include/line-map.h (compare): New function on linenum_type.
+
 2018-02-28  Jonathan Wakely  <jwakely@redhat.com>
 
        PR preprocessor/84517
index f6242fc675df2baf83961bc35e8c6f5d85a63004..d6cf81627cc21cbcbd4f1105a4398cd3847a0ae0 100644 (file)
@@ -49,6 +49,18 @@ along with this program; see the file COPYING3.  If not see
 /* The type of line numbers.  */
 typedef unsigned int linenum_type;
 
+/* A function for for use by qsort for comparing line numbers.  */
+
+inline int compare (linenum_type lhs, linenum_type rhs)
+{
+  /* Avoid truncation issues by using long long for the comparison,
+     and only consider the sign of the result.  */
+  long long diff = (long long)lhs - (long long)rhs;
+  if (diff)
+    return diff > 0 ? 1 : -1;
+  return 0;
+}
+
 /* Reason for creating a new line map with linemap_add.  LC_ENTER is
    when including a new file, e.g. a #include directive in C.
    LC_LEAVE is when reaching a file's end.  LC_RENAME is when a file