Fix linemap corruption after very wide source lines (PR c++/72803)
authorDavid Malcolm <dmalcolm@redhat.com>
Sat, 7 Jan 2017 21:33:59 +0000 (21:33 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Sat, 7 Jan 2017 21:33:59 +0000 (21:33 +0000)
PR c++/72803 describes an issue where a fix-it hint is to be emitted at
column 512 of a 511-column source line, leading to an ICE.

The root cause is a bug in linemap_line_start, when transitioning from
lines >= 512 in width to narrow lines.

The wide line in the reproducer has a line map with:
  m_column_and_range_bits = 15, m_range_bits = 5
giving 10 effective bits for representing columns, so that columns <= 1023
can be represented.

When parsing the following line,
  linemap_line_start (..., ..., max_column_hint=0);
is called.  This leads to the "add_map" logic, due to this condition:
      || (max_column_hint <= 80 && effective_column_bits >= 10)
i.e. the new line is sufficiently narrower than the old one to
potentially use a new linemap (so as to conserve values within the
location_t space).

It then attempts to avoid allocating a new line map.  Part of the logic
to determine if we really need a new line map is this condition:
   SOURCE_COLUMN (map, highest) >= (1U << column_bits)
The above condition is incorrect: we need to determine if the highest
column we've handed out will fit within the proposed *effective* column
bits, but "column_bits" here is the column plus the range bits, rather
than just the column bits.

Hence in this case linemap_line_start erroneously decides that we don't
need a new line map, and updates the column bits within the existing
line map, so any location_t values we've already handed out within it
that are offset from the start by
  >= (1<<new_column_and_range_bits)
effectively change meaning, leading to incorrect line&column information
when decoding them, and various "interesting" ways for the linemap
code to fail.

The fix is to use the effective column bits in the above conditional.

gcc/ChangeLog:
PR c++/72803
* input.c (selftest::test_accessing_ordinary_linemaps): Verify
that the transition from a max line width >= 1<<10 to narrower
lines works correctly.

gcc/testsuite/ChangeLog:
PR c++/72803
* g++.dg/diagnostic/pr72803.C: New test case.

libcpp/ChangeLog:
PR c++/72803
* line-map.c (linemap_line_start): When determining if the highest
column given out so far will fit into a proposed change to the
current map, use the effective number of column bits, rather than
the total number of column + range bits.

From-SVN: r244199

gcc/ChangeLog
gcc/input.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/diagnostic/pr72803.C [new file with mode: 0644]
libcpp/ChangeLog
libcpp/line-map.c

index 9f7f0900becc1a45875f28eabc27a4f7be3f8304..38b6102f3f72d5e10a2273971063d20e8bf171e4 100644 (file)
@@ -1,3 +1,10 @@
+2017-01-07  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c++/72803
+       * input.c (selftest::test_accessing_ordinary_linemaps): Verify
+       that the transition from a max line width >= 1<<10 to narrower
+       lines works correctly.
+
 2017-01-07  Alexandre Oliva <aoliva@redhat.com>
 
        * doc/options.texi (PerFunction): New.
index 195f72e06197112bd7c060b7ea041c3f0d82243e..bbb6abb8878b3588c3bd48fc03acf44d30dc85fd 100644 (file)
@@ -1688,6 +1688,17 @@ test_accessing_ordinary_linemaps (const line_table_case &case_)
   linemap_line_start (line_table, 3, 2000);
   location_t loc_e = linemap_position_for_column (line_table, 700);
 
+  /* Transitioning back to a short line.  */
+  linemap_line_start (line_table, 4, 0);
+  location_t loc_back_to_short = linemap_position_for_column (line_table, 100);
+
+  if (should_have_column_data_p (loc_back_to_short))
+    {
+      /* Verify that we switched to short lines in the linemap.  */
+      line_map_ordinary *map = LINEMAPS_LAST_ORDINARY_MAP (line_table);
+      ASSERT_EQ (7, map->m_column_and_range_bits - map->m_range_bits);
+    }
+
   linemap_add (line_table, LC_LEAVE, false, NULL, 0);
 
   /* Multiple files.  */
@@ -1702,6 +1713,7 @@ test_accessing_ordinary_linemaps (const line_table_case &case_)
   assert_loceq ("foo.c", 2, 1, loc_c);
   assert_loceq ("foo.c", 2, 17, loc_d);
   assert_loceq ("foo.c", 3, 700, loc_e);
+  assert_loceq ("foo.c", 4, 100, loc_back_to_short);
   assert_loceq ("bar.c", 1, 150, loc_f);
 
   ASSERT_FALSE (is_location_from_builtin_token (loc_a));
index 340ec41053902058baa7ff1d0c27a02084cc5daa..1f6100bd323e59ff319d31b2802b171690d389f7 100644 (file)
@@ -1,3 +1,8 @@
+2017-01-07  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c++/72803
+       * g++.dg/diagnostic/pr72803.C: New test case.
+
 2017-01-07  Martin Sebor  <msebor@redhat.com>
 
        * gcc.dg/attr-alloc_size-3.c: Remove regex made redundant by r243461.
diff --git a/gcc/testsuite/g++.dg/diagnostic/pr72803.C b/gcc/testsuite/g++.dg/diagnostic/pr72803.C
new file mode 100644 (file)
index 0000000..0a9a390
--- /dev/null
@@ -0,0 +1,9 @@
+/* Long line, with a close brace at column 511, hence with the insertion
+   point for the missing semicolon at column 512.  */
+class test {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  }
+# 1 "" 1
+// The line directive appears to be necessary to trigger the ICE
+// { dg-error "style of line directive is a GCC extension" "" { target *-*-* } .-2 }
+
+/* Verify that we get the correct line and column for the diagnostic.  */
+// { dg-error "512: expected .;. after class definition" "" { target *-*-* } 3 }
index e9055bb92a41ae33525764f4d941c3de2973ea14..082291d56489b67595a9bc23b48ab6508d6cb26b 100644 (file)
@@ -1,3 +1,11 @@
+2017-01-07  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c++/72803
+       * line-map.c (linemap_line_start): When determining if the highest
+       column given out so far will fit into a proposed change to the
+       current map, use the effective number of column bits, rather than
+       the total number of column + range bits.
+
 2017-01-01  Jakub Jelinek  <jakub@redhat.com>
 
        Update copyright years.
index 77beaffd5bf8f8b0cab9f534a1806a526709fe39..b410c00e36724523bf89794a186aa272547f3f5c 100644 (file)
@@ -752,7 +752,7 @@ linemap_line_start (struct line_maps *set, linenum_type to_line,
         single line we can sometimes just increase its column_bits instead. */
       if (line_delta < 0
          || last_line != ORDINARY_MAP_STARTING_LINE_NUMBER (map)
-         || SOURCE_COLUMN (map, highest) >= (1U << column_bits)
+         || SOURCE_COLUMN (map, highest) >= (1U << (column_bits - range_bits))
          || range_bits < map->m_range_bits)
        map = linemap_check_ordinary
                (const_cast <line_map *>