From b1179e6963099daef392f82c5342b2acf2417629 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 11 Dec 2018 11:10:14 -0500
Subject: [PATCH 09/21] {APPROVED} C++: better locations for bogus
 initializations (PR c++/88375)

PR c++/88375 reports that errors relating to invalid conversions in
initializations are reported at unhelpfully vague locations, as in
e.g.:

enum struct a : int {
  one, two
};

struct foo {
  int e1, e2;
  a e3;
} arr[] = {
  { 1, 2, a::one },
  { 3, a::two },
  { 4, 5, a::two }
};

for which g++ trunk emits the vague:

pr88375.cc:12:1: error: cannot convert 'a' to 'int' in initialization
   12 | };
      | ^

with the error at the final closing brace.

This patch uses location information for the initializers, converting the
above to:

pr88375.cc:10:11: error: cannot convert 'a' to 'int' in initialization
   10 |   { 3, a::two },
      |        ~~~^~~
      |           |
      |           a

highlighting which subexpression is problematic, and its type.

Ideally we'd also issue a note showing the field decl being initialized,
but that turned out to be more invasive.

This patch relies on:

 "[PATCH 1/2] v3: C++: more location wrapper nodes (PR c++/43064, PR c++/43486)"
     https://gcc.gnu.org/ml/gcc-patches/2018-12/msg00500.html

gcc/cp/ChangeLog:
	PR c++/88375
	* typeck.c (convert_for_assignment): Capture location of rhs
	before stripping, and if available.  Use the location when
	complaining about bad conversions, labelling it with the
	rhstype if the location was present.
	* typeck2.c (digest_init_r): Capture location of init before
	stripping.

gcc/testsuite/ChangeLog:
	PR c++/88375
	* g++.dg/init/pr88375-2.C: New test.
	* g++.dg/init/pr88375.C: New test.
---
 gcc/cp/typeck.c                       | 43 ++++++++++++++++++++---------------
 gcc/cp/typeck2.c                      | 10 ++++----
 gcc/testsuite/g++.dg/init/pr88375-2.C | 41 +++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/init/pr88375.C   | 26 +++++++++++++++++++++
 4 files changed, 98 insertions(+), 22 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/init/pr88375-2.C
 create mode 100644 gcc/testsuite/g++.dg/init/pr88375.C

diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 0985186..a3d760b 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -8839,6 +8839,7 @@ convert_for_assignment (tree type, tree rhs,
   enum tree_code coder;
 
   location_t rhs_loc = EXPR_LOC_OR_LOC (rhs, input_location);
+  bool has_loc = EXPR_LOCATION (rhs) != UNKNOWN_LOCATION;
   /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue,
      but preserve location wrappers.  */
   if (TREE_CODE (rhs) == NON_LVALUE_EXPR
@@ -8879,7 +8880,7 @@ convert_for_assignment (tree type, tree rhs,
   if (coder == VOID_TYPE)
     {
       if (complain & tf_error)
-	error ("void value not ignored as it ought to be");
+	error_at (rhs_loc, "void value not ignored as it ought to be");
       return error_mark_node;
     }
 
@@ -8951,35 +8952,43 @@ convert_for_assignment (tree type, tree rhs,
 					     rhstype, type,
 					     fndecl, parmnum);
 	      else
-		switch (errtype)
-		  {
+		{
+		  range_label_for_type_mismatch label (rhstype, type);
+		  gcc_rich_location richloc (rhs_loc, has_loc ? &label : NULL);
+		  switch (errtype)
+		    {
 		    case ICR_DEFAULT_ARGUMENT:
-		      error ("cannot convert %qH to %qI in default argument",
-			     rhstype, type);
+		      error_at (&richloc,
+				"cannot convert %qH to %qI in default argument",
+				rhstype, type);
 		      break;
 		    case ICR_ARGPASS:
-		      error ("cannot convert %qH to %qI in argument passing",
-			     rhstype, type);
+		      error_at (&richloc,
+				"cannot convert %qH to %qI in argument passing",
+				rhstype, type);
 		      break;
 		    case ICR_CONVERTING:
-		      error ("cannot convert %qH to %qI",
-			     rhstype, type);
+		      error_at (&richloc, "cannot convert %qH to %qI",
+				rhstype, type);
 		      break;
 		    case ICR_INIT:
-		      error ("cannot convert %qH to %qI in initialization",
-			     rhstype, type);
+		      error_at (&richloc,
+				"cannot convert %qH to %qI in initialization",
+				rhstype, type);
 		      break;
 		    case ICR_RETURN:
-		      error ("cannot convert %qH to %qI in return",
-			     rhstype, type);
+		      error_at (&richloc, "cannot convert %qH to %qI in return",
+				rhstype, type);
 		      break;
 		    case ICR_ASSIGN:
-		      error ("cannot convert %qH to %qI in assignment",
-			     rhstype, type);
+		      error_at (&richloc,
+				"cannot convert %qH to %qI in assignment",
+				rhstype, type);
 		      break;
 		    default:
 		      gcc_unreachable();
 		  }
+		}
 	      if (TYPE_PTR_P (rhstype)
 		  && TYPE_PTR_P (type)
 		  && CLASS_TYPE_P (TREE_TYPE (rhstype))
@@ -9046,9 +9055,7 @@ convert_for_assignment (tree type, tree rhs,
       && TREE_CODE (TREE_TYPE (rhs)) != BOOLEAN_TYPE
       && (complain & tf_warning))
     {
-      location_t loc = cp_expr_loc_or_loc (rhs, input_location);
-
-      warning_at (loc, OPT_Wparentheses,
+      warning_at (rhs_loc, OPT_Wparentheses,
 		  "suggest parentheses around assignment used as truth value");
       TREE_NO_WARNING (rhs) = 1;
     }
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index c1fa4a9..209832b 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1050,14 +1050,16 @@ digest_init_r (tree type, tree init, int nested, int flags,
 					complain))
     return error_mark_node;
 
+  location_t loc = cp_expr_loc_or_loc (init, input_location);
+
+  tree stripped_init = init;
+
   /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue
      (g++.old-deja/g++.law/casts2.C).  */
   if (TREE_CODE (init) == NON_LVALUE_EXPR)
-    init = TREE_OPERAND (init, 0);
-
-  location_t loc = cp_expr_loc_or_loc (init, input_location);
+    stripped_init = TREE_OPERAND (init, 0);
 
-  tree stripped_init = tree_strip_any_location_wrapper (init);
+  stripped_init = tree_strip_any_location_wrapper (stripped_init);
 
   /* Initialization of an array of chars from a string constant. The initializer
      can be optionally enclosed in braces, but reshape_init has already removed
diff --git a/gcc/testsuite/g++.dg/init/pr88375-2.C b/gcc/testsuite/g++.dg/init/pr88375-2.C
new file mode 100644
index 0000000..97ed72e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/pr88375-2.C
@@ -0,0 +1,41 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-fdiagnostics-show-caret" }
+
+enum struct a : int {
+  one, two
+};
+
+constexpr int fn () { return 42; }
+
+struct foo {
+  int e1, e2;
+  a e3;
+} arr[] = {
+  { 3, a::two }, // { dg-error "11: cannot convert 'a' to 'int' in initialization" }
+  /* { dg-begin-multiline-output "" }
+   { 3, a::two },
+        ~~~^~~
+           |
+           a
+     { dg-end-multiline-output "" } */
+  { 6, 7, fn() }, // { dg-error "13: cannot convert 'int' to 'a' in initialization" }
+  /* { dg-begin-multiline-output "" }
+   { 6, 7, fn() },
+           ~~^~
+             |
+             int
+     { dg-end-multiline-output "" } */
+};
+
+struct bar {
+  const char *f1;
+  int f2;
+} arr_2[] = {
+  { 42 }, // { dg-error "5: invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   { 42 },
+     ^~
+     |
+     int
+     { dg-end-multiline-output "" } */
+};
diff --git a/gcc/testsuite/g++.dg/init/pr88375.C b/gcc/testsuite/g++.dg/init/pr88375.C
new file mode 100644
index 0000000..21dd6c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/pr88375.C
@@ -0,0 +1,26 @@
+// { dg-do compile { target c++11 } }
+
+enum struct a : int {
+  one, two
+};
+
+constexpr int fn () { return 42; }
+
+struct foo {
+  int e1, e2;
+  a e3;
+} arr[] = {
+  { 1, 2, a::one },
+  { 3, a::two }, // { dg-error "11: cannot convert 'a' to 'int' in initialization" }
+  { 6, 7, 8 }, // { dg-error "11: cannot convert 'int' to 'a' in initialization" }
+  { 6, 7, fn() }, // { dg-error "13: cannot convert 'int' to 'a' in initialization" }
+};
+
+struct bar {
+  const char *f1;
+  int f2;
+} arr_2[] = {
+  { "hello world", 42 },
+  { 42 }, // { dg-error "5: invalid conversion from 'int' to 'const char\\*'" }
+  { "hello", "world" }, // { dg-error "14: invalid conversion from 'const char\\*' to 'int'" }
+};
-- 
1.8.5.3

