public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH/RFC] C++ FE: expression ranges (work in progress)
@ 2015-11-07  3:40 David Malcolm
  2015-11-15  4:43 ` [PATCH/RFC] C++ FE: expression ranges (v2) David Malcolm
  0 siblings, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-11-07  3:40 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

Caveat: this patch is a work-in-progress, but I thought it was worth
posting to check that the concept is OK.

This patch builds on top of the patch kit:
"[PATCH 00/10] Overhaul of diagnostics (v5)"
  https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02536.html
of which patches 1-4 are now in trunk.

Note that the above kit generalizes the meaning of location_t to mean
a combination of both a caret location *and* a source range.

This patch is analogous to:
"[PATCH 06/10] Track expression ranges in C frontend"
  https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02535.html

in that it adds range information to the various expressions, but this
time for the C++ frontend.

The benefit here would be e.g. to highlight the LHS and RHS of a bad
binary expression:

error: no match for ‘operator+’ (operand types are ‘int’ and ‘s’)
   return (first_function_with_a_very_long_name ()
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           + second_function_with_a_very_long_name ());
           ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

where exactly which is the LHS and RHS can be unclear when dealing with
complicated nesting of expressions.

As with the C frontend, there's an issue with tree nodes that
don't have locations: VAR_DECL, INTEGER_CST, etc:

  int test (int foo)
  {
    return foo * 100;
           ^^^   ^^^
  }

where we'd like to access the source spelling ranges of the expressions
during parsing, so that we can use them when reporting parser errors.

I resolved this for the C frontend by augmenting struct c_expr.
However, the C++ parser works purely on "tree".  Hence this patch
introduces a new class "cp_expr", which bundles a tree and a location_t,
with implicit conversion between tree and cp_expr via a ctor,
an operator tree () etc, effectively preserving location information for
things like VAR_DECL during parsing.  (I first attempted explicit
conversions, but it turned out to be a huge task; doing it implicitly
made it doable).  Some explicit "expr.get_value ()" method calls are
needed for variadic calls e.g.:

        error_at (id_expr_token->location,
                  "local variable %qD may not appear in this context",
  -               decl);
  +               decl.get_value ());

since operator tree () can't be used there.

For tree nodes with a location, the location_t is always the same
as the EXPR_LOCATION of node (and redundant).
For other tree nodes, the location_t is of interest (and is asserted
to not be UNKNOWN_LOCATION).

Most of the time, the cp_expr's location is rather redundant, but
it's essential at the leaves of the parse tree, where it's used for
preserving the locations of the tokens as they become identifiers,
constants, etc: an extra 32-bit value is being stored and passed around
with the tree ptrs.

The new tests were shameless copied from the C FE testsuite, fixing
them up to reflect some apparent differences in caret location between
C and C++ for some expression, and adding some C++-specific constructs
(perhaps it could be done by referencing the C tests in
g++.dg/plugin/plugin.exp, with relative paths, rather than copying them?)

This isn't ready yet:
* it has FIXMEs
* although it seems to bootstrap, it has about 100 regressions in
  g++.sum, and a lot of regressions in obj-c++.sum
* I haven't done performance testing yet

That said, is this the right direction?

Also, in cp_parser_new_expression I attempted to generate meaningful
ranges e.g.:

  int *foo = new int[100];
             ^~~~~~~~~~~~

but it seems to be hard to do this, due to the trailing optional
components; I found myself wanting to ask the lexer for the last
token it consumed (in particular, I'm interested in the
location_t of that token).  Is this a reasonable thing to add to the
lexer?

Thanks

gcc/ChangeLog:
	* convert.c (convert_to_integer): In integer-to-integer
	conversions, preserve the location.

gcc/cp/ChangeLog:
	* call.c (build_new_op_1): Set the location of the result
	of cp_build_unary_op.
	* cp-tree.h (class cp_expr): New class.
	(perform_koenig_lookup): Convert return type and param from tree
	to cp_expr.
	(finish_increment_expr): Likewise.
	(finish_unary_op_expr): Likewise.
	(finish_id_expression): Likewise for return type.
	(build_class_member_access_expr): Likewise for param.
	(finish_class_member_access_expr): Likewise.
	(build_x_unary_op): Likewise.
	(build_c_cast): Likewise.
	(build_x_modify_expr): Likewise for return type.
	* name-lookup.c (lookup_arg_dependent_1): Likewise.
	(lookup_arg_dependent): Likewise; also for local "ret".
	* name-lookup.h (lookup_arg_dependent): Likewise for return type.
	* parser.c (struct cp_parser_expression_stack_entry): Likewise
	for field "lhs".
	(cp_parser_identifier): Likewise for return type.  Use cp_expr
	ctor to preserve the token's location.
	(cp_parser_string_literal): Likewise, building up a meaningful
	location for the case where a compound string literal is built by
	concatentation.
	(cp_parser_userdef_char_literal): Likewise for return type.
	(cp_parser_userdef_numeric_literal): Likewise.
	(cp_parser_fold_expression): Likewise.
	(cp_parser_primary_expression): Likewise, and for locals "expr",
	"lam", "id_expression", "decl".
	Use cp_expr ctor when parsing literals, to preserve the spelling
	location of the token.  Preserve the locations of parentheses.
	FIXME: disable call to objc_lookup_ivar.
	(cp_parser_primary_expression): Convert return type from tree to
	cp_expr.
	(cp_parser_id_expression): Likewise.
	(cp_parser_unqualified_id): Likewise.  Also for local "id".
	(cp_parser_postfix_expression): Likewise, also for local
	"postfix_expression".  Preserve start location.  Use it
	to construct spelling ranges for C++-style casts.  Pass on the
	location of the closing parenthesis of a call site to
	cp_parser_parenthesized_expression_list, and use it to build
	a source range for a call.  Use cp_expr in ternary expression.
	(cp_parser_postfix_dot_deref_expression): Convert param from tree to
	cp_expr.
	(cp_parser_parenthesized_expression_list): Add "close_paren_loc"
	out-param, and write back to it.
	(cp_parser_unary_expression): Convert return type from tree to
	cp_expr.  Also for locals "cast_expression" and "expression".
	Generate suitable locations for cast expressions.
	Call protected_set_expr_location on expression.
	(cp_parser_new_expression): FIXME: unfinished attempt to
	generate meaningful locations (see note in blurb above).
	(cp_parser_cast_expression): Convert return type from tree to
	cp_expr; also for local "expr".  Use the paren location to generate a
	meaningful range for the expression.
	(cp_parser_binary_expression): Convert return type from tree to
	cp_expr; also for local "rhs".  Generate a meaningful location
	for the expression, and use it.  Replace call to
	protected_set_expr_location by converting a build2 to a
	build2_loc.
	(cp_parser_question_colon_clause): Convert return type from tree
	to cp_expr; also for local "assignmend_expr".  Set the spelling
	range of the expression.
	(cp_parser_assignment_expression): Likewise for return type and
	locals "expr" and "rhs".  Build a meaningful spelling range for
	the expression, and call protected_set_expr_location since
	build_x_modify_expr appears not to always honor "loc".
	(cp_parser_expression): Likewise for return type and locals
	"expression" and "assignment_expression".  Build a meaningful
	spelling range for assignment expressions.
	(cp_parser_constant_expression): Likewise for return type and
	local "expression".
	(cp_parser_lambda_expression): Likewise for return type.
	(cp_parser_operator_function_id): Likewise.
	(cp_parser_operator): Likewise.  Generate a meaningful range,
	using cp_expr's ctor to return it.
	(cp_parser_initializer_clause): Likewise for local "initializer".
	(cp_parser_lookup_name): Likewise for return type.  Use cp_expr's
	ctor to preserve the location_t of the name.
	(cp_parser_simple_cast_expression): Likewise for return type.
	* semantics.c (perform_koenig_lookup): Likewise for return type
	and param.
	(finish_increment_expr): Likewise.  Generate a meaningful spelling
	range.
	(finish_unary_op_expr): Likewise.
	(finish_id_expression): Likewise.
	* typeck.c (build_class_member_access_expr): Likewise for param;
	generate range for result.
	(finish_class_member_access_expr): Likewise.
	(cp_build_binary_op): Convert a build2 to a build2_loc.
	(build_x_unary_op): Convert param from tree to cp_expr.
	(build_nop): Use the location of the input expression for the
	NOP_EXPR.
	(build_c_cast): Use the provided location_t for the result.
	Provide an overloaded variant that takes a cp_esxpr and returns
	a cp_expr.
	(build_x_modify_expr): Convert return type from tree to cp_expr.
	Call protected_set_expr_location on the result of
	cp_build_modify_expr.
	* typeck2.c (build_x_arrow): Call protected_set_expr_location on
	the result of cp_build_indirect_ref.

gcc/testsuite/ChangeLog:
	* g++.dg/plugin/diagnostic-test-expressions-1.c: New file.
	* g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c: New file.
	* g++.dg/plugin/plugin.exp (plugin_test_list): Add the above.

gcc/ChangeLog:
	* tree.c (get_pure_location): Make non-static.
	(set_source_range): Return the resulting location_t.
	(make_location): New function.
	* tree.h (get_pure_location): New decl.
	(set_source_range): Convert return type from void to location_t.
	(make_location): New decl.
---
 gcc/convert.c                                      |   2 +-
 gcc/cp/call.c                                      |   6 +-
 gcc/cp/cp-tree.h                                   |  98 +++-
 gcc/cp/name-lookup.c                               |   6 +-
 gcc/cp/name-lookup.h                               |   2 +-
 gcc/cp/parser.c                                    | 299 ++++++++----
 gcc/cp/semantics.c                                 |  29 +-
 gcc/cp/typeck.c                                    |  60 ++-
 gcc/cp/typeck2.c                                   |   4 +-
 .../g++.dg/plugin/diagnostic-test-expressions-1.c  | 535 +++++++++++++++++++++
 .../diagnostic_plugin_test_tree_expression_range.c |  98 ++++
 gcc/testsuite/g++.dg/plugin/plugin.exp             |   5 +-
 gcc/tree.c                                         |  25 +-
 gcc/tree.h                                         |  10 +-
 14 files changed, 1026 insertions(+), 153 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
 create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c

diff --git a/gcc/convert.c b/gcc/convert.c
index bff2978..f17a328 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -612,7 +612,7 @@ convert_to_integer (tree type, tree expr)
 	  else
 	    code = NOP_EXPR;
 
-	  return fold_build1 (code, type, expr);
+	  return fold_build1_loc (loc, code, type, expr);
 	}
 
       /* If TYPE is an enumeral type or a type with a precision less
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index f8db2df..e377a87 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5762,7 +5762,11 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
     case REALPART_EXPR:
     case IMAGPART_EXPR:
     case ABS_EXPR:
-      return cp_build_unary_op (code, arg1, candidates != 0, complain);
+      {
+	tree result = cp_build_unary_op (code, arg1, candidates != 0, complain);
+	protected_set_expr_location (result, loc);
+	return result;
+      }
 
     case ARRAY_REF:
       return cp_build_array_ref (input_location, arg1, arg2, complain);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f650c76..6a75ce9 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -40,6 +40,86 @@ c-common.h, not after.
 #include "c-family/c-common.h"
 #include "diagnostic.h"
 
+/* A tree node, together with a location, so that we can track locations
+   (and ranges) during parsing.
+
+   The location is redundant for node kinds that have locations,
+   but not all node kinds do (e.g. constants, and references to
+   params, locals, etc), so we stash a copy here.  */
+
+class cp_expr
+{
+public:
+  cp_expr () :
+    m_value (NULL), m_loc (UNKNOWN_LOCATION) {}
+
+  cp_expr (tree value) :
+    m_value (value), m_loc (EXPR_LOCATION (m_value))
+  {
+#if 0
+    /* FIXME: various assertions can be put in here when debugging,
+       for tracking down where location information gets thrown
+       away (during a trip through a purely "tree" value).  */
+    if (m_value && m_value != error_mark_node)
+      {
+	if (TREE_CODE (m_value) == FUNCTION_DECL)
+	  return; // for now
+	gcc_assert (CAN_HAVE_LOCATION_P (m_value));
+	//gcc_assert (m_loc != UNKNOWN_LOCATION);
+      }
+#endif
+  }
+
+  cp_expr (tree value, location_t loc):
+    m_value (value), m_loc (loc)
+  {
+    if (m_value)
+      gcc_assert (m_loc != UNKNOWN_LOCATION);
+  }
+
+  cp_expr (const cp_expr &other) :
+    m_value (other.m_value), m_loc (other.m_loc) {}
+
+  /* Implicit conversions to tree.  */
+  operator tree () const { return m_value; }
+  tree & operator* () { return m_value; }
+  tree & operator-> () { return m_value; }
+
+  tree get_value () const { return m_value; }
+  location_t get_location () const { return m_loc; }
+  location_t get_start () const
+  {
+    source_range src_range = get_range_from_loc (line_table, m_loc);
+    return src_range.m_start;
+  }
+  location_t get_finish () const
+  {
+    source_range src_range = get_range_from_loc (line_table, m_loc);
+    return src_range.m_finish;
+  }
+
+  void set_location (location_t loc)
+  {
+    protected_set_expr_location (m_value, loc);
+    m_loc = loc;
+  }
+
+  void set_range (location_t start, location_t finish)
+  {
+    m_loc = set_source_range (m_value, start, finish);
+  }
+
+ private:
+  tree m_value;
+  location_t m_loc;
+};
+
+inline bool
+operator == (const cp_expr &lhs, tree rhs)
+{
+  return lhs.get_value () == rhs;
+}
+
 #include "name-lookup.h"
 
 /* Usage of TREE_LANG_FLAG_?:
@@ -6250,15 +6330,15 @@ extern tree finish_stmt_expr_expr		(tree, tree);
 extern tree finish_stmt_expr			(tree, bool);
 extern tree stmt_expr_value_expr		(tree);
 bool empty_expr_stmt_p				(tree);
-extern tree perform_koenig_lookup		(tree, vec<tree, va_gc> *,
+extern cp_expr perform_koenig_lookup		(cp_expr, vec<tree, va_gc> *,
 						 tsubst_flags_t);
 extern tree finish_call_expr			(tree, vec<tree, va_gc> **, bool,
 						 bool, tsubst_flags_t);
 extern tree finish_template_variable		(tree, tsubst_flags_t = tf_warning_or_error);
-extern tree finish_increment_expr		(tree, enum tree_code);
+extern cp_expr finish_increment_expr		(cp_expr, enum tree_code);
 extern tree finish_this_expr			(void);
 extern tree finish_pseudo_destructor_expr       (tree, tree, tree, location_t);
-extern tree finish_unary_op_expr		(location_t, enum tree_code, tree,
+extern cp_expr finish_unary_op_expr		(location_t, enum tree_code, cp_expr,
 						 tsubst_flags_t);
 extern tree finish_compound_literal		(tree, tree, tsubst_flags_t);
 extern tree finish_fname			(tree);
@@ -6272,7 +6352,7 @@ extern tree finish_base_specifier		(tree, tree, bool);
 extern void finish_member_declaration		(tree);
 extern bool outer_automatic_var_p		(tree);
 extern tree process_outer_var_ref		(tree, tsubst_flags_t);
-extern tree finish_id_expression		(tree, tree, tree,
+extern cp_expr finish_id_expression		(tree, tree, tree,
 						 cp_id_kind *,
 						 bool, bool, bool *,
 						 bool, bool, bool, bool,
@@ -6510,9 +6590,9 @@ extern tree unlowered_expr_type                 (const_tree);
 extern tree decay_conversion			(tree,
                                                  tsubst_flags_t,
                                                  bool = true);
-extern tree build_class_member_access_expr      (tree, tree, tree, bool,
+extern tree build_class_member_access_expr      (cp_expr, tree, tree, bool,
 						 tsubst_flags_t);
-extern tree finish_class_member_access_expr     (tree, tree, bool, 
+extern tree finish_class_member_access_expr     (cp_expr, tree, bool,
 						 tsubst_flags_t);
 extern tree build_x_indirect_ref		(location_t, tree,
 						 ref_operator, tsubst_flags_t);
@@ -6534,7 +6614,7 @@ extern tree build_x_binary_op			(location_t,
 extern tree build_x_array_ref			(location_t, tree, tree,
 						 tsubst_flags_t);
 extern tree build_x_unary_op			(location_t,
-						 enum tree_code, tree,
+						 enum tree_code, cp_expr,
                                                  tsubst_flags_t);
 extern tree cp_build_addr_expr			(tree, tsubst_flags_t);
 extern tree cp_build_unary_op                   (enum tree_code, tree, int, 
@@ -6554,8 +6634,10 @@ extern tree build_static_cast			(tree, tree, tsubst_flags_t);
 extern tree build_reinterpret_cast		(tree, tree, tsubst_flags_t);
 extern tree build_const_cast			(tree, tree, tsubst_flags_t);
 extern tree build_c_cast			(location_t, tree, tree);
+extern cp_expr build_c_cast			(location_t loc, tree type,
+						 cp_expr expr);
 extern tree cp_build_c_cast			(tree, tree, tsubst_flags_t);
-extern tree build_x_modify_expr			(location_t, tree,
+extern cp_expr build_x_modify_expr		(location_t, tree,
 						 enum tree_code, tree,
 						 tsubst_flags_t);
 extern tree cp_build_modify_expr		(tree, enum tree_code, tree,
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index b503012..35c27d5 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -5666,7 +5666,7 @@ arg_assoc (struct arg_lookup *k, tree n)
 /* Performs Koenig lookup depending on arguments, where fns
    are the functions found in normal lookup.  */
 
-static tree
+static cp_expr
 lookup_arg_dependent_1 (tree name, tree fns, vec<tree, va_gc> *args)
 {
   struct arg_lookup k;
@@ -5727,10 +5727,10 @@ lookup_arg_dependent_1 (tree name, tree fns, vec<tree, va_gc> *args)
 
 /* Wrapper for lookup_arg_dependent_1.  */
 
-tree
+cp_expr
 lookup_arg_dependent (tree name, tree fns, vec<tree, va_gc> *args)
 {
-  tree ret;
+  cp_expr ret;
   bool subtime;
   subtime = timevar_cond_start (TV_NAME_LOOKUP);
   ret = lookup_arg_dependent_1 (name, fns, args);
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index d430edb..d2453e9 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -347,7 +347,7 @@ extern void do_toplevel_using_decl (tree, tree, tree);
 extern void do_local_using_decl (tree, tree, tree);
 extern tree do_class_using_decl (tree, tree);
 extern void do_using_directive (tree);
-extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
+extern cp_expr lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
 extern bool is_associated_namespace (tree, tree);
 extern void parse_using_directive (tree, tree);
 extern tree innermost_non_namespace_value (tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index d4ef7f9..b83fe06 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -1784,7 +1784,7 @@ struct cp_parser_expression_stack_entry
 {
   /* Left hand side of the binary operation we are currently
      parsing.  */
-  tree lhs;
+  cp_expr lhs;
   /* Original tree code for left hand side, if it was a binary
      expression itself (used for -Wparentheses).  */
   enum tree_code lhs_type;
@@ -1938,15 +1938,15 @@ static cp_parser *cp_parser_new
 
 /* Lexical conventions [gram.lex]  */
 
-static tree cp_parser_identifier
+static cp_expr cp_parser_identifier
   (cp_parser *);
-static tree cp_parser_string_literal
+static cp_expr cp_parser_string_literal
   (cp_parser *, bool, bool, bool);
-static tree cp_parser_userdef_char_literal
+static cp_expr cp_parser_userdef_char_literal
   (cp_parser *);
 static tree cp_parser_userdef_string_literal
   (tree);
-static tree cp_parser_userdef_numeric_literal
+static cp_expr cp_parser_userdef_numeric_literal
   (cp_parser *);
 
 /* Basic concepts [gram.basic]  */
@@ -1956,11 +1956,11 @@ static bool cp_parser_translation_unit
 
 /* Expressions [gram.expr]  */
 
-static tree cp_parser_primary_expression
+static cp_expr cp_parser_primary_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
-static tree cp_parser_id_expression
+static cp_expr cp_parser_id_expression
   (cp_parser *, bool, bool, bool *, bool, bool);
-static tree cp_parser_unqualified_id
+static cp_expr cp_parser_unqualified_id
   (cp_parser *, bool, bool, bool, bool);
 static tree cp_parser_nested_name_specifier_opt
   (cp_parser *, bool, bool, bool, bool);
@@ -1968,19 +1968,19 @@ static tree cp_parser_nested_name_specifier
   (cp_parser *, bool, bool, bool, bool);
 static tree cp_parser_qualifying_entity
   (cp_parser *, bool, bool, bool, bool, bool);
-static tree cp_parser_postfix_expression
+static cp_expr cp_parser_postfix_expression
   (cp_parser *, bool, bool, bool, bool, cp_id_kind *);
 static tree cp_parser_postfix_open_square_expression
   (cp_parser *, tree, bool, bool);
 static tree cp_parser_postfix_dot_deref_expression
-  (cp_parser *, enum cpp_ttype, tree, bool, cp_id_kind *, location_t);
+  (cp_parser *, enum cpp_ttype, cp_expr, bool, cp_id_kind *, location_t);
 static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
-  (cp_parser *, int, bool, bool, bool *, bool = false);
+  (cp_parser *, int, bool, bool, bool *, bool = false, location_t * = NULL);
 /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
 enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
 static void cp_parser_pseudo_destructor_name
   (cp_parser *, tree, tree *, tree *);
-static tree cp_parser_unary_expression
+static cp_expr cp_parser_unary_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
 static enum tree_code cp_parser_unary_operator
   (cp_token *);
@@ -1998,23 +1998,23 @@ static vec<tree, va_gc> *cp_parser_new_initializer
   (cp_parser *);
 static tree cp_parser_delete_expression
   (cp_parser *);
-static tree cp_parser_cast_expression
+static cp_expr cp_parser_cast_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
-static tree cp_parser_binary_expression
+static cp_expr cp_parser_binary_expression
   (cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *);
 static tree cp_parser_question_colon_clause
-  (cp_parser *, tree);
-static tree cp_parser_assignment_expression
+  (cp_parser *, cp_expr);
+static cp_expr cp_parser_assignment_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
 static enum tree_code cp_parser_assignment_operator_opt
   (cp_parser *);
-static tree cp_parser_expression
+static cp_expr cp_parser_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
-static tree cp_parser_constant_expression
+static cp_expr cp_parser_constant_expression
   (cp_parser *, bool = false, bool * = NULL);
 static tree cp_parser_builtin_offsetof
   (cp_parser *);
-static tree cp_parser_lambda_expression
+static cp_expr cp_parser_lambda_expression
   (cp_parser *);
 static void cp_parser_lambda_introducer
   (cp_parser *, tree);
@@ -2169,7 +2169,7 @@ static void cp_parser_function_body
   (cp_parser *, bool);
 static tree cp_parser_initializer
   (cp_parser *, bool *, bool *);
-static tree cp_parser_initializer_clause
+static cp_expr cp_parser_initializer_clause
   (cp_parser *, bool *);
 static tree cp_parser_braced_list
   (cp_parser*, bool*);
@@ -2237,9 +2237,9 @@ static tree cp_parser_mem_initializer_id
 
 /* Overloading [gram.over] */
 
-static tree cp_parser_operator_function_id
+static cp_expr cp_parser_operator_function_id
   (cp_parser *);
-static tree cp_parser_operator
+static cp_expr cp_parser_operator
   (cp_parser *);
 
 /* Templates [gram.temp] */
@@ -2410,7 +2410,7 @@ static tree cp_parser_objc_struct_declaration
 
 /* Utility Routines */
 
-static tree cp_parser_lookup_name
+static cp_expr cp_parser_lookup_name
   (cp_parser *, tree, enum tag_types, bool, bool, bool, tree *, location_t);
 static tree cp_parser_lookup_name_simple
   (cp_parser *, tree, location_t);
@@ -2420,7 +2420,7 @@ static bool cp_parser_check_declarator_template_parameters
   (cp_parser *, cp_declarator *, location_t);
 static bool cp_parser_check_template_parameters
   (cp_parser *, unsigned, location_t, cp_declarator *);
-static tree cp_parser_simple_cast_expression
+static cp_expr cp_parser_simple_cast_expression
   (cp_parser *);
 static tree cp_parser_global_scope_opt
   (cp_parser *, bool);
@@ -3666,7 +3666,7 @@ cp_parser_pop_lexer (cp_parser *parser)
 /* Parse an identifier.  Returns an IDENTIFIER_NODE representing the
    identifier.  */
 
-static tree
+static cp_expr
 cp_parser_identifier (cp_parser* parser)
 {
   cp_token *token;
@@ -3674,7 +3674,10 @@ cp_parser_identifier (cp_parser* parser)
   /* Look for the identifier.  */
   token = cp_parser_require (parser, CPP_NAME, RT_NAME);
   /* Return the value.  */
-  return token ? token->u.value : error_mark_node;
+  if (token)
+    return cp_expr (token->u.value, token->location);
+  else
+    return error_mark_node;
 }
 
 /* Parse a sequence of adjacent string constants.  Returns a
@@ -3691,7 +3694,7 @@ cp_parser_identifier (cp_parser* parser)
    This code is largely lifted from lex_string() in c-lex.c.
 
    FUTURE: ObjC++ will need to handle @-strings here.  */
-static tree
+static cp_expr
 cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 			  bool lookup_udlit = true)
 {
@@ -3713,6 +3716,8 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
       return error_mark_node;
     }
 
+  location_t loc = tok->location;
+
   if (cpp_userdef_string_p (tok->type))
     {
       string_tree = USERDEF_LITERAL_VALUE (tok->u.value);
@@ -3750,11 +3755,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
     }
   else
     {
+      location_t last_tok_loc;
       gcc_obstack_init (&str_ob);
       count = 0;
 
       do
 	{
+	  last_tok_loc = tok->location;
 	  cp_lexer_consume_token (parser->lexer);
 	  count++;
 	  str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
@@ -3809,6 +3816,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 	}
       while (cp_parser_is_string_literal (tok));
 
+      /* A string literal built by concatenation has its caret=start at
+	 the start of the initial string, and its finish at the finish of
+	 the final string literal.  */
+      loc = make_location (loc, loc,
+			   get_range_from_loc (line_table,
+					       last_tok_loc).m_finish);
+
       strs = (cpp_string *) obstack_finish (&str_ob);
     }
 
@@ -3861,7 +3875,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
   if (count > 1)
     obstack_free (&str_ob, 0);
 
-  return value;
+  return cp_expr (value, loc);
 }
 
 /* Look up a literal operator with the name and the exact arguments.  */
@@ -3912,7 +3926,7 @@ lookup_literal_operator (tree name, vec<tree, va_gc> *args)
 /* Parse a user-defined char constant.  Returns a call to a user-defined
    literal operator taking the character as an argument.  */
 
-static tree
+static cp_expr
 cp_parser_userdef_char_literal (cp_parser *parser)
 {
   cp_token *token = cp_lexer_consume_token (parser->lexer);
@@ -4004,7 +4018,7 @@ make_string_pack (tree value)
 /* Parse a user-defined numeric constant.  returns a call to a user-defined
    literal operator.  */
 
-static tree
+static cp_expr
 cp_parser_userdef_numeric_literal (cp_parser *parser)
 {
   cp_token *token = cp_lexer_consume_token (parser->lexer);
@@ -4405,7 +4419,7 @@ cp_parser_fold_operator (cp_parser *parser)
 
    Note that the '(' and ')' are matched in primary expression. */
 
-static tree
+static cp_expr
 cp_parser_fold_expression (cp_parser *parser, tree expr1)
 {
   cp_id_kind pidk;
@@ -4525,7 +4539,7 @@ cp_parser_fold_expression (cp_parser *parser, tree expr1)
    Returns a representation of the expression.  Upon return, *IDK
    indicates what kind of id-expression (if any) was present.  */
 
-static tree
+static cp_expr
 cp_parser_primary_expression (cp_parser *parser,
 			      bool address_p,
 			      bool cast_p,
@@ -4610,7 +4624,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	  if (!cast_p)
 	    cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
 	}
-      return token->u.value;
+      return cp_expr (token->u.value, token->location);
 
     case CPP_CHAR_USERDEF:
     case CPP_CHAR16_USERDEF:
@@ -4668,9 +4682,11 @@ cp_parser_primary_expression (cp_parser *parser,
 	}
       /* Otherwise it's a normal parenthesized expression.  */
       {
-	tree expr;
+	cp_expr expr;
 	bool saved_greater_than_is_operator_p;
 
+	location_t open_paren_loc = token->location;
+
 	/* Consume the `('.  */
 	cp_lexer_consume_token (parser->lexer);
 	/* Within a parenthesized expression, a `>' token is always
@@ -4715,7 +4731,11 @@ cp_parser_primary_expression (cp_parser *parser,
 	   template-parameter-list now.  */
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
+
 	/* Consume the `)'.  */
+	token = cp_lexer_peek_token (parser->lexer);
+	location_t close_paren_loc = token->location;
+	expr.set_range (open_paren_loc, close_paren_loc);
 	if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
 	    && !cp_parser_uncommitted_to_tentative_parse_p (parser))
 	  cp_parser_skip_to_end_of_statement (parser);
@@ -4735,7 +4755,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	      return msg;
 	    /* ... else, fall though to see if it's a lambda.  */
 	  }
-	tree lam = cp_parser_lambda_expression (parser);
+	cp_expr lam = cp_parser_lambda_expression (parser);
 	/* Don't warn about a failed tentative parse.  */
 	if (cp_parser_error_occurred (parser))
 	  return error_mark_node;
@@ -4756,20 +4776,20 @@ cp_parser_primary_expression (cp_parser *parser,
 	  /* These two are the boolean literals.  */
 	case RID_TRUE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_true_node;
+	  return cp_expr (boolean_true_node, token->location);
 	case RID_FALSE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_false_node;
+	  return cp_expr (boolean_false_node, token->location);
 
 	  /* The `__null' literal.  */
 	case RID_NULL:
 	  cp_lexer_consume_token (parser->lexer);
-	  return null_node;
+	  return cp_expr (null_node, token->location);
 
 	  /* The `nullptr' literal.  */
 	case RID_NULLPTR:
 	  cp_lexer_consume_token (parser->lexer);
-	  return nullptr_node;
+	  return cp_expr (nullptr_node, token->location);
 
 	  /* Recognize the `this' keyword.  */
 	case RID_THIS:
@@ -4917,14 +4937,14 @@ cp_parser_primary_expression (cp_parser *parser,
     case CPP_TEMPLATE_ID:
     case CPP_NESTED_NAME_SPECIFIER:
       {
-	tree id_expression;
-	tree decl;
+      id_expression:
+	cp_expr id_expression;
+	cp_expr decl;
 	const char *error_msg;
 	bool template_p;
 	bool done;
 	cp_token *id_expr_token;
 
-      id_expression:
 	/* Parse the id-expression.  */
 	id_expression
 	  = cp_parser_id_expression (parser,
@@ -4992,9 +5012,13 @@ cp_parser_primary_expression (cp_parser *parser,
 		return objc_build_class_component_ref (id_expression, component);
 	      }
 
+            /* FIXME: disabling for now, as it strips location info, and I'd
+               prefer not to expose cp_expr to c-family for now.  */
+#if 0
 	    /* In Objective-C++, an instance variable (ivar) may be preferred
 	       to whatever cp_parser_lookup_name() found.  */
 	    decl = objc_lookup_ivar (decl, id_expression);
+#endif
 
 	    /* If name lookup gives us a SCOPE_REF, then the
 	       qualifying scope was dependent.  */
@@ -5035,7 +5059,7 @@ cp_parser_primary_expression (cp_parser *parser,
 		  {
 		    error_at (id_expr_token->location,
 			      "local variable %qD may not appear in this context",
-			      decl);
+			      decl.get_value ());
 		    return error_mark_node;
 		  }
 	      }
@@ -5063,7 +5087,7 @@ cp_parser_primary_expression (cp_parser *parser,
     }
 }
 
-static inline tree
+static inline cp_expr
 cp_parser_primary_expression (cp_parser *parser,
 			      bool address_p,
 			      bool cast_p,
@@ -5108,7 +5132,7 @@ cp_parser_primary_expression (cp_parser *parser,
    If DECLARATOR_P is true, the id-expression is appearing as part of
    a declarator, rather than as part of an expression.  */
 
-static tree
+static cp_expr
 cp_parser_id_expression (cp_parser *parser,
 			 bool template_keyword_p,
 			 bool check_dependency_p,
@@ -5243,7 +5267,7 @@ cp_parser_id_expression (cp_parser *parser,
    is true, the unqualified-id is appearing as part of a declarator,
    rather than as part of an expression.  */
 
-static tree
+static cp_expr
 cp_parser_unqualified_id (cp_parser* parser,
 			  bool template_keyword_p,
 			  bool check_dependency_p,
@@ -5506,7 +5530,7 @@ cp_parser_unqualified_id (cp_parser* parser,
     case CPP_KEYWORD:
       if (token->keyword == RID_OPERATOR)
 	{
-	  tree id;
+	  cp_expr id;
 
 	  /* This could be a template-id, so we try that first.  */
 	  cp_parser_parse_tentatively (parser);
@@ -6096,7 +6120,7 @@ cp_parser_compound_literal_p (cp_parser *parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
                               bool member_access_only_p, bool decltype_p,
 			      cp_id_kind * pidk_return)
@@ -6105,13 +6129,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
   location_t loc;
   enum rid keyword;
   cp_id_kind idk = CP_ID_KIND_NONE;
-  tree postfix_expression = NULL_TREE;
+  cp_expr postfix_expression = NULL_TREE;
   bool is_member_access = false;
   int saved_in_statement = -1;
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
   loc = token->location;
+  location_t start_loc = get_range_from_loc (line_table, loc).m_start;
+
   /* Some of the productions are determined by keywords.  */
   keyword = token->keyword;
   switch (keyword)
@@ -6122,7 +6148,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
     case RID_CONSTCAST:
       {
 	tree type;
-	tree expression;
+	cp_expr expression;
 	const char *saved_message;
 	bool saved_in_type_id_in_expr_p;
 
@@ -6155,7 +6181,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* And the expression which is being cast.  */
 	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
 	expression = cp_parser_expression (parser, & idk, /*cast_p=*/true);
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	location_t end_loc = cp_parser_require (parser, CPP_CLOSE_PAREN,
+						RT_CLOSE_PAREN)->location;
 
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
@@ -6188,6 +6215,11 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  default:
 	    gcc_unreachable ();
 	  }
+
+	/* Construct a location ranging from the start of the "*_cast" token
+	   to the final closing paren, with the caret at the start.  */
+	location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc);
+	postfix_expression.set_location (cp_cast_loc);
       }
       break;
 
@@ -6467,6 +6499,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 							postfix_expression,
 							false,
 							decltype_p);
+          postfix_expression.set_range (start_loc,
+                                        postfix_expression.get_location ());
+
 	  idk = CP_ID_KIND_NONE;
           is_member_access = false;
 	  break;
@@ -6480,6 +6515,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    bool saved_non_integral_constant_expression_p = false;
 	    tsubst_flags_t complain = complain_flags (decltype_p);
 	    vec<tree, va_gc> *args;
+	    location_t close_paren_loc;
 
             is_member_access = false;
 
@@ -6499,7 +6535,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 		    (parser, non_attr,
 		     /*cast_p=*/false, /*allow_expansion_p=*/true,
 		     /*non_constant_p=*/NULL,
-		     /*want_literal_zero_p=*/warn_memset_transposed_args));
+		     /*want_literal_zero_p=*/warn_memset_transposed_args,
+		     /*close_paren_loc=*/&close_paren_loc));
 	    if (is_builtin_constant_p)
 	      {
 		parser->integral_constant_expression_p
@@ -6649,7 +6686,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 				    koenig_p,
 				    complain);
 
-	    protected_set_expr_location (postfix_expression, token->location);
+	    location_t combined_loc = make_location (token->location,
+						     start_loc,
+						     close_paren_loc);
+	    protected_set_expr_location (postfix_expression, combined_loc);
 
 	    /* The POSTFIX_EXPRESSION is certainly no longer an id.  */
 	    idk = CP_ID_KIND_NONE;
@@ -6710,7 +6750,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  if (pidk_return != NULL)
 	    * pidk_return = idk;
           if (member_access_only_p)
-            return is_member_access? postfix_expression : error_mark_node;
+            return is_member_access
+              ? postfix_expression
+              : cp_expr (error_mark_node);
           else
             return postfix_expression;
 	}
@@ -6910,7 +6952,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 static tree
 cp_parser_postfix_dot_deref_expression (cp_parser *parser,
 					enum cpp_ttype token_type,
-					tree postfix_expression,
+					cp_expr postfix_expression,
 					bool for_offsetof, cp_id_kind *idk,
 					location_t location)
 {
@@ -6947,7 +6989,7 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
       if (scope == unknown_type_node)
 	{
 	  error_at (location, "%qE does not have class type",
-		    postfix_expression);
+		    postfix_expression.get_value ());
 	  scope = NULL_TREE;
 	}
       /* Unlike the object expression in other contexts, *this is not
@@ -7120,7 +7162,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 bool cast_p,
                                          bool allow_expansion_p,
 					 bool *non_constant_p,
-					 bool want_literal_zero_p)
+					 bool want_literal_zero_p,
+					 location_t *close_paren_loc)
 {
   vec<tree, va_gc> *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
@@ -7267,6 +7310,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 	cp_lexer_consume_token (parser->lexer);
       }
 
+  if (close_paren_loc)
+    *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
     {
       int ending;
@@ -7436,7 +7482,7 @@ cp_parser_pseudo_destructor_name (cp_parser* parser,
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 			    bool address_p, bool cast_p, bool decltype_p)
 {
@@ -7650,8 +7696,8 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
     }
   if (unary_operator != ERROR_MARK)
     {
-      tree cast_expression;
-      tree expression = error_mark_node;
+      cp_expr cast_expression;
+      cp_expr expression = error_mark_node;
       non_integral_constant non_constant_p = NIC_NONE;
       location_t loc = token->location;
       tsubst_flags_t complain = complain_flags (decltype_p);
@@ -7665,6 +7711,11 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 				     /*cast_p=*/false,
 				     /*decltype*/false,
 				     pidk);
+
+      /* Make a location starting at the token (the caret), and
+         extending to the end of the cast_expression.  */
+      loc = make_location (loc, loc, cast_expression.get_finish ());
+
       /* Now, build an appropriate representation.  */
       switch (unary_operator)
 	{
@@ -7700,6 +7751,8 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	  gcc_unreachable ();
 	}
 
+      protected_set_expr_location (expression, loc);
+
       if (non_constant_p != NIC_NONE
 	  && cp_parser_non_integral_constant_expression (parser,
 							 non_constant_p))
@@ -7763,6 +7816,8 @@ cp_parser_new_expression (cp_parser* parser)
   tree nelts = NULL_TREE;
   tree ret;
 
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   /* Look for the optional `::' operator.  */
   global_scope_p
     = (cp_parser_global_scope_opt (parser,
@@ -7857,6 +7912,11 @@ cp_parser_new_expression (cp_parser* parser)
   if (initializer != NULL)
     release_tree_vector (initializer);
 
+  /* FIXME: better location for result; currently just the location of
+     the initial token.
+     There doesn't seem to be an easy way to get at the last consumed
+     token from the lexer, which would give us the finish of the range.  */
+  protected_set_expr_location (ret, start_loc);
   return ret;
 }
 
@@ -8229,7 +8289,7 @@ cp_parser_tokens_start_cast_expression (cp_parser *parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 			   bool decltype_p, cp_id_kind * pidk)
 {
@@ -8237,7 +8297,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
       tree type = NULL_TREE;
-      tree expr = NULL_TREE;
+      cp_expr expr (NULL_TREE);
       int cast_expression = 0;
       const char *saved_message;
 
@@ -8250,7 +8310,9 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
       parser->type_definition_forbidden_message
 	= G_("types may not be defined in casts");
       /* Consume the `('.  */
-      cp_lexer_consume_token (parser->lexer);
+      cp_token *open_paren = cp_lexer_consume_token (parser->lexer);
+      location_t open_paren_loc = open_paren->location;
+
       /* A very tricky bit is that `(struct S) { 3 }' is a
 	 compound-literal (which we permit in C++ as an extension).
 	 But, that construct is not a cast-expression -- it is a
@@ -8352,7 +8414,12 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 		return error_mark_node;
 
 	      /* Perform the cast.  */
-	      expr = build_c_cast (input_location, type, expr);
+	      /* Make a location starting at the open paren (the caret), and
+		 extending to the end of "expr".  */
+	      location_t cast_loc = make_location (open_paren_loc,
+						   open_paren_loc,
+						   expr.get_finish ());
+	      expr = build_c_cast (cast_loc, type, expr);
 	      return expr;
 	    }
 	}
@@ -8445,7 +8512,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
  ? PREC_NOT_OPERATOR					     \
  : binops_by_token[token->type].prec)
 
-static tree
+static cp_expr
 cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 			     bool no_toplevel_fold_p,
 			     bool decltype_p,
@@ -8455,7 +8522,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
   cp_parser_expression_stack stack;
   cp_parser_expression_stack_entry *sp = &stack[0];
   cp_parser_expression_stack_entry current;
-  tree rhs;
+  cp_expr rhs;
   cp_token *token;
   enum tree_code rhs_type;
   enum cp_parser_prec new_prec, lookahead_prec;
@@ -8591,6 +8658,11 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 				      maybe_constant_value (rhs));
 
       overload = NULL;
+
+      location_t combined_loc = make_location (current.loc,
+					       current.lhs.get_start (),
+					       rhs.get_finish ());
+
       /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type ==
 	 ERROR_MARK for everything that is not a binary expression.
 	 This makes warn_about_parentheses miss some warnings that
@@ -8601,18 +8673,18 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
       if (no_toplevel_fold_p
 	  && lookahead_prec <= current.prec
 	  && sp == stack)
-	current.lhs = build2 (current.tree_type,
-			      TREE_CODE_CLASS (current.tree_type)
-			      == tcc_comparison
-			      ? boolean_type_node : TREE_TYPE (current.lhs),
-			      current.lhs, rhs);
+	current.lhs = build2_loc (combined_loc,
+				  current.tree_type,
+				  TREE_CODE_CLASS (current.tree_type)
+				  == tcc_comparison
+				  ? boolean_type_node : TREE_TYPE (current.lhs),
+				  current.lhs, rhs);
       else
-	current.lhs = build_x_binary_op (current.loc, current.tree_type,
+	current.lhs = build_x_binary_op (combined_loc, current.tree_type,
 					 current.lhs, current.lhs_type,
 					 rhs, rhs_type, &overload,
 					 complain_flags (decltype_p));
       current.lhs_type = current.tree_type;
-      protected_set_expr_location (current.lhs, current.loc);
 
       /* If the binary operator required the use of an overloaded operator,
 	 then this expression cannot be an integral constant-expression.
@@ -8629,7 +8701,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
   return current.lhs;
 }
 
-static tree
+static cp_expr
 cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 			     bool no_toplevel_fold_p,
 			     enum cp_parser_prec prec,
@@ -8653,10 +8725,10 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
      ? : assignment-expression */
 
 static tree
-cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
+cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr)
 {
   tree expr;
-  tree assignment_expr;
+  cp_expr assignment_expr;
   struct cp_token *token;
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
@@ -8692,6 +8764,12 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
   assignment_expr = cp_parser_assignment_expression (parser);
   c_inhibit_evaluation_warnings -= logical_or_expr == truthvalue_true_node;
 
+  /* Make a location with the caret at the "?", ranging from the start of
+     the logical_or_expr to the end of the assignment_expr.  */
+  loc = make_location (loc,
+		       logical_or_expr.get_start (),
+		       assignment_expr.get_finish ());
+
   /* Build the conditional-expression.  */
   return build_x_conditional_expr (loc, logical_or_expr,
 				   expr,
@@ -8711,11 +8789,11 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
 
    Returns a representation for the expression.  */
 
-static tree
+static cp_expr
 cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 				 bool cast_p, bool decltype_p)
 {
-  tree expr;
+  cp_expr expr;
 
   /* If the next token is the `throw' keyword, then we're looking at
      a throw-expression.  */
@@ -8747,7 +8825,8 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 	      location_t saved_input_location;
 
 	      /* Parse the right-hand side of the assignment.  */
-	      tree rhs = cp_parser_initializer_clause (parser, &non_constant_p);
+	      cp_expr rhs = cp_parser_initializer_clause (parser,
+							  &non_constant_p);
 
 	      if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
 		maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
@@ -8758,13 +8837,19 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 							      NIC_ASSIGNMENT))
 		return error_mark_node;
 	      /* Build the assignment expression.  Its default
-		 location is the location of the '=' token.  */
+		 location is the location of the '=' token as the
+		 caret, ranging from the start of the lhs to the
+		 end of the rhs.  */
 	      saved_input_location = input_location;
+	      loc = make_location (loc,
+				   expr.get_start (),
+				   rhs.get_finish ());
 	      input_location = loc;
 	      expr = build_x_modify_expr (loc, expr,
 					  assignment_operator,
 					  rhs,
 					  complain_flags (decltype_p));
+	      protected_set_expr_location (expr, loc);
 	      input_location = saved_input_location;
 	    }
 	}
@@ -8874,16 +8959,16 @@ cp_parser_assignment_operator_opt (cp_parser* parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
 		      bool cast_p, bool decltype_p)
 {
-  tree expression = NULL_TREE;
+  cp_expr expression = NULL_TREE;
   location_t loc = UNKNOWN_LOCATION;
 
   while (true)
     {
-      tree assignment_expression;
+      cp_expr assignment_expression;
 
       /* Parse the next assignment-expression.  */
       assignment_expression
@@ -8905,9 +8990,17 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
       if (!expression)
 	expression = assignment_expression;
       else
-	expression = build_x_compound_expr (loc, expression,
-					    assignment_expression,
-					    complain_flags (decltype_p));
+	{
+	  /* Create a location with caret at the comma, ranging
+	     from the start of the LHS to the end of the RHS.  */
+	  loc = make_location (loc,
+			       expression.get_start (),
+			       assignment_expression.get_finish ());
+	  expression = build_x_compound_expr (loc, expression,
+					      assignment_expression,
+					      complain_flags (decltype_p));
+	  expression.set_location (loc);
+	}
       /* If the next token is not a comma, or we're in a fold-expression, then
 	 we are done with the expression.  */
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)
@@ -8934,7 +9027,7 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
   constant, *NON_CONSTANT_P is set to TRUE.  If ALLOW_NON_CONSTANT_P
   is false, NON_CONSTANT_P should be NULL.  */
 
-static tree
+static cp_expr
 cp_parser_constant_expression (cp_parser* parser,
 			       bool allow_non_constant_p,
 			       bool *non_constant_p)
@@ -8942,7 +9035,7 @@ cp_parser_constant_expression (cp_parser* parser,
   bool saved_integral_constant_expression_p;
   bool saved_allow_non_integral_constant_expression_p;
   bool saved_non_integral_constant_expression_p;
-  tree expression;
+  cp_expr expression;
 
   /* It might seem that we could simply parse the
      conditional-expression, and then check to see if it were
@@ -9312,7 +9405,7 @@ finish_lambda_scope (void)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_lambda_expression (cp_parser* parser)
 {
   tree lambda_expr = build_lambda_expr ();
@@ -13314,7 +13407,7 @@ cp_parser_mem_initializer_id (cp_parser* parser)
    Returns an IDENTIFIER_NODE for the operator which is a
    human-readable spelling of the identifier, e.g., `operator +'.  */
 
-static tree
+static cp_expr
 cp_parser_operator_function_id (cp_parser* parser)
 {
   /* Look for the `operator' keyword.  */
@@ -13354,7 +13447,7 @@ cp_literal_operator_id (const char* name)
    Returns an IDENTIFIER_NODE for the operator which is a
    human-readable spelling of the identifier, e.g., `operator +'.  */
 
-static tree
+static cp_expr
 cp_parser_operator (cp_parser* parser)
 {
   tree id = NULL_TREE;
@@ -13363,6 +13456,9 @@ cp_parser_operator (cp_parser* parser)
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
+
+  location_t start_loc = token->location;
+
   /* Figure out which operator we have.  */
   switch (token->type)
     {
@@ -13379,7 +13475,7 @@ cp_parser_operator (cp_parser* parser)
 	  break;
 
 	/* Consume the `new' or `delete' token.  */
-	cp_lexer_consume_token (parser->lexer);
+	location_t end_loc = cp_lexer_consume_token (parser->lexer)->location;
 
 	/* Peek at the next token.  */
 	token = cp_lexer_peek_token (parser->lexer);
@@ -13390,7 +13486,8 @@ cp_parser_operator (cp_parser* parser)
 	    /* Consume the `[' token.  */
 	    cp_lexer_consume_token (parser->lexer);
 	    /* Look for the `]' token.  */
-	    cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
+	    end_loc = cp_parser_require (parser, CPP_CLOSE_SQUARE,
+                                         RT_CLOSE_SQUARE)->location;
 	    id = ansi_opname (op == NEW_EXPR
 			      ? VEC_NEW_EXPR : VEC_DELETE_EXPR);
 	  }
@@ -13398,7 +13495,9 @@ cp_parser_operator (cp_parser* parser)
 	else
 	  id = ansi_opname (op);
 
-	return id;
+	location_t loc = make_location (start_loc, start_loc, end_loc);
+
+	return cp_expr (id, loc);
       }
 
     case CPP_PLUS:
@@ -13644,7 +13743,7 @@ cp_parser_operator (cp_parser* parser)
       id = error_mark_node;
     }
 
-  return id;
+  return cp_expr (id, start_loc);
 }
 
 /* Parse a template-declaration.
@@ -20318,10 +20417,10 @@ cp_parser_initializer (cp_parser* parser, bool* is_direct_init,
 
    Otherwise, calls cp_parser_braced_list.  */
 
-static tree
+static cp_expr
 cp_parser_initializer_clause (cp_parser* parser, bool* non_constant_p)
 {
-  tree initializer;
+  cp_expr initializer;
 
   /* Assume the expression is constant.  */
   *non_constant_p = false;
@@ -24088,7 +24187,7 @@ cp_parser_nested_requirement (cp_parser *parser)
    TREE_LIST of candidates if name-lookup results in an ambiguity, and
    NULL_TREE otherwise.  */
 
-static tree
+static cp_expr
 cp_parser_lookup_name (cp_parser *parser, tree name,
 		       enum tag_types tag_type,
 		       bool is_template,
@@ -24330,7 +24429,7 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
 
   maybe_record_typedef_use (decl);
 
-  return decl;
+  return cp_expr (decl, name_location);
 }
 
 /* Like cp_parser_lookup_name, but for use in the typical case where
@@ -25332,7 +25431,7 @@ cp_parser_single_declaration (cp_parser* parser,
 
 /* Parse a cast-expression that is not the operand of a unary "&".  */
 
-static tree
+static cp_expr
 cp_parser_simple_cast_expression (cp_parser *parser)
 {
   return cp_parser_cast_expression (parser, /*address_p=*/false,
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index c1f4330..0d0b053 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2159,8 +2159,8 @@ empty_expr_stmt_p (tree expr_stmt)
    the function (or functions) to call; ARGS are the arguments to the
    call.  Returns the functions to be considered by overload resolution.  */
 
-tree
-perform_koenig_lookup (tree fn, vec<tree, va_gc> *args,
+cp_expr
+perform_koenig_lookup (cp_expr fn, vec<tree, va_gc> *args,
 		       tsubst_flags_t complain)
 {
   tree identifier = NULL_TREE;
@@ -2455,10 +2455,15 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
    is indicated by CODE, which should be POSTINCREMENT_EXPR or
    POSTDECREMENT_EXPR.)  */
 
-tree
-finish_increment_expr (tree expr, enum tree_code code)
+cp_expr
+finish_increment_expr (cp_expr expr, enum tree_code code)
 {
-  return build_x_unary_op (input_location, code, expr, tf_warning_or_error);
+  /* input_location holds the location of the trailing operator token.  */
+  cp_expr result = build_x_unary_op (input_location, code, expr,
+				     tf_warning_or_error);
+  result.set_range (expr.get_start (),
+		    get_range_from_loc (line_table, input_location).m_finish);
+  return result;
 }
 
 /* Finish a use of `this'.  Returns an expression for `this'.  */
@@ -2552,16 +2557,18 @@ finish_pseudo_destructor_expr (tree object, tree scope, tree destructor,
 
 /* Finish an expression of the form CODE EXPR.  */
 
-tree
-finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
+cp_expr
+finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr,
 		      tsubst_flags_t complain)
 {
-  tree result = build_x_unary_op (loc, code, expr, complain);
+  location_t combined_loc = make_location (op_loc,
+					   op_loc, expr.get_finish ());
+  tree result = build_x_unary_op (combined_loc, code, expr, complain);
   if ((complain & tf_warning)
       && TREE_OVERFLOW_P (result) && !TREE_OVERFLOW_P (expr))
     overflow_warning (input_location, result);
 
-  return result;
+  return cp_expr (result, combined_loc);
 }
 
 /* Finish a compound-literal expression.  TYPE is the type to which
@@ -3302,7 +3309,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
    the use of "this" explicit.
 
    Upon return, *IDK will be filled in appropriately.  */
-tree
+cp_expr
 finish_id_expression (tree id_expression,
 		      tree decl,
 		      tree scope,
@@ -3647,7 +3654,7 @@ finish_id_expression (tree id_expression,
 	}
     }
 
-  return decl;
+  return cp_expr (decl, location);
 }
 
 /* Implement the __typeof keyword: Return the type of EXPR, suitable for
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 9e6f949..1cdbfbc 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2269,7 +2269,7 @@ lookup_anon_field (tree t, tree type)
    functions indicated by MEMBER.  */
 
 tree
-build_class_member_access_expr (tree object, tree member,
+build_class_member_access_expr (cp_expr object, tree member,
 				tree access_path, bool preserve_reference,
 				tsubst_flags_t complain)
 {
@@ -2299,10 +2299,10 @@ build_class_member_access_expr (tree object, tree member,
 	      && CLASS_TYPE_P (TREE_TYPE (object_type)))
 	    error ("request for member %qD in %qE, which is of pointer "
 		   "type %qT (maybe you meant to use %<->%> ?)",
-		   member, object, object_type);
+		   member, object.get_value (), object_type);
 	  else
 	    error ("request for member %qD in %qE, which is of non-class "
-		   "type %qT", member, object, object_type);
+		   "type %qT", member, object.get_value (), object_type);
 	}
       return error_mark_node;
     }
@@ -2443,7 +2443,12 @@ build_class_member_access_expr (tree object, tree member,
 	  member_type = cp_build_qualified_type (member_type, type_quals);
 	}
 
-      result = build3_loc (input_location, COMPONENT_REF, member_type,
+      location_t combined_loc =
+	make_location (input_location,
+		       object.get_start (),
+		       get_range_from_loc (line_table,
+					   input_location).m_finish);
+      result = build3_loc (combined_loc, COMPONENT_REF, member_type,
 			   object, member, NULL_TREE);
       result = fold_if_not_in_template (result);
 
@@ -2633,7 +2638,7 @@ check_template_keyword (tree decl)
    be a template via the use of the "A::template B" syntax.  */
 
 tree
-finish_class_member_access_expr (tree object, tree name, bool template_p,
+finish_class_member_access_expr (cp_expr object, tree name, bool template_p,
 				 tsubst_flags_t complain)
 {
   tree expr;
@@ -2667,7 +2672,7 @@ finish_class_member_access_expr (tree object, tree name, bool template_p,
 	      && TYPE_P (TREE_OPERAND (name, 0))
 	      && dependent_type_p (TREE_OPERAND (name, 0))))
 	return build_min_nt_loc (UNKNOWN_LOCATION, COMPONENT_REF,
-				 object, name, NULL_TREE);
+				 object.get_value (), name, NULL_TREE);
       object = build_non_dependent_expr (object);
     }
   else if (c_dialect_objc ()
@@ -2690,10 +2695,10 @@ finish_class_member_access_expr (tree object, tree name, bool template_p,
 	      && CLASS_TYPE_P (TREE_TYPE (object_type)))
 	    error ("request for member %qD in %qE, which is of pointer "
 		   "type %qT (maybe you meant to use %<->%> ?)",
-		   name, object, object_type);
+		   name, object.get_value (), object_type);
 	  else
 	    error ("request for member %qD in %qE, which is of non-class "
-		   "type %qT", name, object, object_type);
+		   "type %qT", name, object.get_value (), object_type);
 	}
       return error_mark_node;
     }
@@ -5108,7 +5113,7 @@ cp_build_binary_op (location_t location,
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
-  result = build2 (resultcode, build_type, op0, op1);
+  result = build2_loc (location, resultcode, build_type, op0, op1);
   result = fold_if_not_in_template (result);
   if (final_type != 0)
     result = cp_convert (final_type, result, complain);
@@ -5255,7 +5260,7 @@ pointer_diff (tree op0, tree op1, tree ptrtype, tsubst_flags_t complain)
    and XARG is the operand.  */
 
 tree
-build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
+build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
 		  tsubst_flags_t complain)
 {
   tree orig_expr = xarg;
@@ -5265,7 +5270,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
   if (processing_template_decl)
     {
       if (type_dependent_expression_p (xarg))
-	return build_min_nt_loc (loc, code, xarg, NULL_TREE);
+	return build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE);
 
       xarg = build_non_dependent_expr (xarg);
     }
@@ -5300,7 +5305,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 		error (DECL_CONSTRUCTOR_P (fn)
 		       ? G_("taking address of constructor %qE")
 		       : G_("taking address of destructor %qE"),
-		       xarg);
+		       xarg.get_value ());
 	      return error_mark_node;
 	    }
 	}
@@ -5316,7 +5321,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 	      if (complain & tf_error)
 		{
 		  error ("invalid use of %qE to form a "
-			 "pointer-to-member-function", xarg);
+			 "pointer-to-member-function", xarg.get_value ());
 		  if (TREE_CODE (xarg) != OFFSET_REF)
 		    inform (input_location, "  a qualified-id is required");
 		}
@@ -5327,7 +5332,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 	      if (complain & tf_error)
 		error ("parentheses around %qE cannot be used to form a"
 		       " pointer-to-member-function",
-		       xarg);
+		       xarg.get_value ());
 	      else
 		return error_mark_node;
 	      PTRMEM_OK_P (xarg) = 1;
@@ -5424,7 +5429,7 @@ build_nop (tree type, tree expr)
 {
   if (type == error_mark_node || error_operand_p (expr))
     return expr;
-  return build1 (NOP_EXPR, type, expr);
+  return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr);
 }
 
 /* Take the address of ARG, whatever that means under C++ semantics.
@@ -7246,9 +7251,23 @@ build_const_cast (tree type, tree expr, tsubst_flags_t complain)
 /* Like cp_build_c_cast, but for the c-common bits.  */
 
 tree
-build_c_cast (location_t /*loc*/, tree type, tree expr)
+build_c_cast (location_t loc, tree type, tree expr)
 {
-  return cp_build_c_cast (type, expr, tf_warning_or_error);
+  tree result = cp_build_c_cast (type, expr, tf_warning_or_error);
+  protected_set_expr_location (result, loc);
+  return result;
+}
+
+/* Like the "build_c_cast" used for c-common, but using cp_expr to
+   preserve location information even for tree nodes that don't
+   support it.  */
+
+cp_expr
+build_c_cast (location_t loc, tree type, cp_expr expr)
+{
+  tree result = cp_build_c_cast (type, expr, tf_warning_or_error);
+  protected_set_expr_location (result, loc);
+  return cp_expr (result, loc);
 }
 
 /* Build an expression representing an explicit C-style cast to type
@@ -7756,7 +7775,7 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
   return result;
 }
 
-tree
+cp_expr
 build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 		     tree rhs, tsubst_flags_t complain)
 {
@@ -7776,7 +7795,10 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 	  return rval;
 	}
     }
-  return cp_build_modify_expr (lhs, modifycode, rhs, complain);
+
+  tree result = cp_build_modify_expr (lhs, modifycode, rhs, complain);
+  protected_set_expr_location (result, loc);
+  return result;
 }
 
 /* Helper function for get_delta_difference which assumes FROM is a base
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 2c9143e..88e482c 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1739,7 +1739,9 @@ build_x_arrow (location_t loc, tree expr, tsubst_flags_t complain)
 	  return expr;
 	}
 
-      return cp_build_indirect_ref (last_rval, RO_NULL, complain);
+      tree result = cp_build_indirect_ref (last_rval, RO_NULL, complain);
+      protected_set_expr_location (result, loc);
+      return result;
     }
 
   if (complain & tf_error)
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
new file mode 100644
index 0000000..e34a66a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
@@ -0,0 +1,535 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret" } */
+
+/* This is a collection of unittests to verify that we're correctly
+   capturing the source code ranges of various kinds of expression.
+
+   It uses the various "diagnostic_test_*_expression_range_plugin"
+   plugins which handles "__emit_expression_range" by generating a warning
+   at the given source range of the input argument.  Each of the
+   different plugins do this at a different phase of the internal
+   representation (tree, gimple, etc), so we can verify that the
+   source code range information is valid at each phase.
+
+   We want to accept an expression of any type.  To do this in C, we
+   use variadic arguments, but C requires at least one argument before
+   the ellipsis, so we have a dummy one.  */
+
+extern void __emit_expression_range (int dummy, ...);
+
+int global;
+
+void test_parentheses (int a, int b)
+{
+  __emit_expression_range (0, (a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) );
+                               ~~~^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, (a + b) * (a - b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) * (a - b) );
+                               ~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, !(a && b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !(a && b) );
+                               ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Postfix expressions.  ************************************************/
+
+void test_array_reference (int *arr)
+{
+  __emit_expression_range (0, arr[100] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, arr[100] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+}
+
+int test_function_call (int p, int q, int r)
+{
+  __emit_expression_range (0, test_function_call (p, q, r) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, test_function_call (p, q, r) );
+                               ~~~~~~~~~~~~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+  return 0;
+}
+
+struct test_struct
+{
+  int field;
+};
+
+int test_structure_references (struct test_struct *ptr)
+{
+  struct test_struct local;
+  local.field = 42;
+
+  __emit_expression_range (0, local.field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, local.field );
+                               ~~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, ptr->field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ptr->field );
+                               ~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+int test_postfix_incdec (int i)
+{
+  __emit_expression_range (0, i++ ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i++ );
+                               ~^~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, i-- ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i-- );
+                               ~^~
+   { dg-end-multiline-output "" } */
+}
+
+/* Unary operators.  ****************************************************/
+
+int test_prefix_incdec (int i)
+{
+  __emit_expression_range (0, ++i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ++i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, --i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, --i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_address_operator (void)
+{
+  __emit_expression_range (0, &global ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &global );
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_indirection (int *ptr)
+{
+  __emit_expression_range (0, *ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *ptr );
+                               ^~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_unary_minus (int i)
+{
+  __emit_expression_range (0, -i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, -i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_ones_complement (int i)
+{
+  __emit_expression_range (0, ~i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ~i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_negation (int flag)
+{
+  __emit_expression_range (0, !flag ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !flag );
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Casts.  ****************************************************/
+
+void test_cast (void *ptr)
+{
+  __emit_expression_range (0, (int *)ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (int *)ptr );
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, *(int *)0xdeadbeef ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *(int *)0xdeadbeef );
+                               ^~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* Binary operators.  *******************************************/
+
+void test_multiplicative_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs * rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs * rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs / rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs / rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs % rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs % rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_additive_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs + rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs + rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs - rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs - rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_shift_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs << rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs << rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >> rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >> rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_relational_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs < rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs < rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs > rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs > rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs <= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs <= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_equality_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs == rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs == rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs != rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs != rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_bitwise_binary_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs & rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs & rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs ^ rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs ^ rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs | rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs | rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs && rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs && rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs || rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs || rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Conditional operator.  *******************************************/
+
+void test_conditional_operators (int flag, int on_true, int on_false)
+{
+  __emit_expression_range (0, flag ? on_true : on_false ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, flag ? on_true : on_false );
+                               ~~~~~^~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Assignment expressions.  *******************************************/
+
+void test_assignment_expressions (int dest, int other)
+{
+  __emit_expression_range (0, dest = other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest = other );
+                               ~~~~~^~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest *= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest *= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest /= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest /= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest %= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest %= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest += other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest += other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest -= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest -= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest <<= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest <<= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest >>= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest >>= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest &= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest &= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest ^= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest ^= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest |= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest |= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Comma operator.  *******************************************/
+
+void test_comma_operator (int a, int b)
+{
+  __emit_expression_range (0, (a++, a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a++, a + b) );
+                               ~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Literals.  **************************************************/
+
+/* We can't test the ranges of literals directly, since the underlying
+   tree nodes don't retain a location.  However, we can test that they
+   have ranges during parsing by building compound expressions using
+   them, and verifying the ranges of the compound expressions.  */
+
+void test_string_literals (int i)
+{
+  __emit_expression_range (0, "foo"[i] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, "foo"[i] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, &"foo" "bar" ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &"foo" "bar" );
+                               ^~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Examples of non-trivial expressions.  ****************************/
+
+extern double sqrt (double x);
+
+void test_quadratic (double a, double b, double c)
+{
+  __emit_expression_range (0, b * b - 4 * a * c ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, b * b - 4 * a * c );
+                               ~~~~~~^~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0,
+     (-b + sqrt (b * b - 4 * a * c))
+     / (2 * a)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+      (-b + sqrt (b * b - 4 * a * c))
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      / (2 * a));
+      ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* C++-specific expresssions. ****************************************/
+
+void test_cp_literal_keywords (int a, int b)
+{
+  this; /* { dg-error "invalid use of 'this' in non-member function" } */
+/* { dg-begin-multiline-output "" }
+   this;
+   ^~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+class base {
+ public:
+  base ();
+  base (int i);
+  virtual ~base ();
+};
+class derived : public base { ~derived (); };
+
+void test_cp_casts (base *ptr)
+{
+  __emit_expression_range (0, dynamic_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dynamic_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, static_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, static_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, reinterpret_cast <int *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, reinterpret_cast <int *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, const_cast <base *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, const_cast <base *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_new (void)
+{
+  /* TODO: currently these only cover the first token; see the FIXME in
+     cp_parser_new_expression.  */
+
+  __emit_expression_range (0, ::new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ::new base);
+                               ^~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base);
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base));
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base (42));
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base) (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base) (42));
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  /* TODO: placement new.  */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
new file mode 100644
index 0000000..89cc95a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
@@ -0,0 +1,98 @@
+/* This plugin verifies the source-code location ranges of
+   expressions, at the pre-gimplification tree stage.  */
+/* { dg-options "-O" } */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "print-tree.h"
+
+int plugin_is_GPL_compatible;
+
+static void
+emit_warning (location_t loc)
+{
+  source_range src_range = get_range_from_loc (line_table, loc);
+  warning_at (loc, 0,
+	      "tree range %i:%i-%i:%i",
+	      LOCATION_LINE (src_range.m_start),
+	      LOCATION_COLUMN (src_range.m_start),
+	      LOCATION_LINE (src_range.m_finish),
+	      LOCATION_COLUMN (src_range.m_finish));
+}
+
+tree
+cb_walk_tree_fn (tree * tp, int * walk_subtrees,
+		 void * data ATTRIBUTE_UNUSED)
+{
+  if (TREE_CODE (*tp) != CALL_EXPR)
+    return NULL_TREE;
+
+  tree call_expr = *tp;
+  tree fn = CALL_EXPR_FN (call_expr);
+  if (TREE_CODE (fn) != ADDR_EXPR)
+    return NULL_TREE;
+  fn = TREE_OPERAND (fn, 0);
+  if (TREE_CODE (fn) != FUNCTION_DECL)
+    return NULL_TREE;
+  if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fn)), "__emit_expression_range"))
+    return NULL_TREE;
+
+  /* Get arg 1; print it! */
+  tree arg = CALL_EXPR_ARG (call_expr, 1);
+
+  emit_warning (EXPR_LOCATION (arg));
+
+  return NULL_TREE;
+}
+
+static void
+callback (void *gcc_data, void *user_data)
+{
+  tree fndecl = (tree)gcc_data;
+  walk_tree (&DECL_SAVED_TREE (fndecl), cb_walk_tree_fn, NULL, NULL);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  register_callback (plugin_name,
+		     PLUGIN_PRE_GENERICIZE,
+		     callback,
+		     NULL);
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
index 3ed1397..2266380 100644
--- a/gcc/testsuite/g++.dg/plugin/plugin.exp
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -62,7 +62,10 @@ set plugin_test_list [list \
     { dumb_plugin.c dumb-plugin-test-1.C } \
     { header_plugin.c header-plugin-test.C } \
     { decl_plugin.c decl-plugin-test.C } \
-    { def_plugin.c def-plugin-test.C } ]
+    { def_plugin.c def-plugin-test.C } \
+    { diagnostic_plugin_test_tree_expression_range.c \
+	  diagnostic-test-expressions-1.c } \
+]
 
 foreach plugin_test $plugin_test_list {
     # Replace each source file with its full-path name
diff --git a/gcc/tree.c b/gcc/tree.c
index 4ec4a38..a919cc7 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13653,7 +13653,7 @@ nonnull_arg_p (const_tree arg)
   return false;
 }
 
-static location_t
+location_t
 get_pure_location (location_t loc)
 {
   if (IS_ADHOC_LOC (loc))
@@ -13680,20 +13680,20 @@ set_block (location_t loc, tree block)
   return COMBINE_LOCATION_DATA (line_table, pure_loc, src_range, block);
 }
 
-void
+location_t
 set_source_range (tree expr, location_t start, location_t finish)
 {
   source_range src_range;
   src_range.m_start = start;
   src_range.m_finish = finish;
-  set_source_range (expr, src_range);
+  return set_source_range (expr, src_range);
 }
 
-void
+location_t
 set_source_range (tree expr, source_range src_range)
 {
   if (!EXPR_P (expr))
-    return;
+    return UNKNOWN_LOCATION;
 
   location_t pure_loc = get_pure_location (EXPR_LOCATION (expr));
   location_t adhoc = COMBINE_LOCATION_DATA (line_table,
@@ -13701,6 +13701,21 @@ set_source_range (tree expr, source_range src_range)
 					    src_range,
 					    NULL);
   SET_EXPR_LOCATION (expr, adhoc);
+  return adhoc;
+}
+
+location_t
+make_location (location_t caret, location_t start, location_t finish)
+{
+  location_t pure_loc = get_pure_location (caret);
+  source_range src_range;
+  src_range.m_start = start;
+  src_range.m_finish = finish;
+  location_t combined_loc = COMBINE_LOCATION_DATA (line_table,
+						   pure_loc,
+						   src_range,
+						   NULL);
+  return combined_loc;
 }
 
 #include "gt-tree.h"
diff --git a/gcc/tree.h b/gcc/tree.h
index 7d20f74..1f32511 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5158,6 +5158,9 @@ type_with_alias_set_p (const_tree t)
 }
 
 extern location_t
+get_pure_location (location_t loc);
+
+extern location_t
 set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
@@ -5166,10 +5169,10 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
 
 extern bool nonnull_arg_p (const_tree);
 
-extern void
+extern location_t
 set_source_range (tree expr, location_t start, location_t finish);
 
-extern void
+extern location_t
 set_source_range (tree expr, source_range src_range);
 
 static inline source_range
@@ -5179,4 +5182,7 @@ get_decl_source_range (tree decl)
   return get_range_from_loc (line_table, loc);
 }
 
+extern location_t
+make_location (location_t caret, location_t start, location_t finish);
+
 #endif  /* GCC_TREE_H  */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-07  3:40 [PATCH/RFC] C++ FE: expression ranges (work in progress) David Malcolm
@ 2015-11-15  4:43 ` David Malcolm
  2015-11-19 20:46   ` Jason Merrill
  0 siblings, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-11-15  4:43 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

(v2 of the patch, fixing some issues, and rebasing to be on top of
r230393 i.e. after the merge of delayed-folding).

This patch is analogous to:
"[PATCH 06/10] Track expression ranges in C frontend"
  https://gcc.gnu.org/ml/gcc-patches/2015-10/msg02535.html

in that it adds range information to the various expressions, but this
time for the C++ frontend.

The benefit here would be e.g. to highlight the LHS and RHS of a bad
binary expression:

error: no match for ‘operator+’ (operand types are ‘int’ and ‘s’)
   return (first_function_with_a_very_long_name ()
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           + second_function_with_a_very_long_name ());
           ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

where exactly which is the LHS and RHS can be unclear when dealing with
complicated nesting of expressions.

As with the C frontend, there's an issue with tree nodes that
don't have locations: VAR_DECL, INTEGER_CST, etc:

  int test (int foo)
  {
    return foo * 100;
           ^^^   ^^^
  }

where we'd like to access the source spelling ranges of the expressions
during parsing, so that we can use them when reporting parser errors.

I resolved this for the C frontend by augmenting struct c_expr.
However, the C++ parser works purely on "tree".  Hence this patch
introduces a new class "cp_expr", which bundles a tree and a location_t,
with implicit conversion between tree and cp_expr via a ctor,
an operator tree () etc, effectively preserving location information for
things like VAR_DECL during parsing.  (I first attempted explicit
conversions, but it turned out to be a huge task; doing it implicitly
made it doable).  Some explicit "expr.get_value ()" method calls are
needed for variadic calls e.g.:

        error_at (id_expr_token->location,
                  "local variable %qD may not appear in this context",
  -               decl);
  +               decl.get_value ());

since operator tree () can't be used there.

For tree nodes with a location, the location_t is always the same
as the EXPR_LOCATION of node (and redundant).
For other tree nodes, the location_t is of interest (and is asserted
to not be UNKNOWN_LOCATION).

Most of the time, the cp_expr's location is rather redundant, but
it's essential at the leaves of the parse tree, where it's used for
preserving the locations of the tokens as they become identifiers,
constants, etc: an extra 32-bit value is being stored and passed around
with the tree ptrs.

The new tests were shameless copied from the C FE testsuite, fixing
them up to reflect some apparent differences in caret location between
C and C++ for some expression, and adding some C++-specific constructs
(perhaps it could be done by referencing the C tests in
g++.dg/plugin/plugin.exp, with relative paths, rather than copying them?)

This bootstraps (x86_64-pc-linux-gnu) but isn't quite ready yet:
* it has some FIXMEs
* it has regressions for 8 test files (for a total of 139 new FAILs in
  g++.sum); these appear to all relate to locations of error messages
* no performance testing yet

That said, is this the right direction?

Also, in cp_parser_new_expression I attempted to generate meaningful
ranges e.g.:

  int *foo = new int[100];
             ^~~~~~~~~~~~

but it seems to be hard to do this, due to the trailing optional
components; I found myself wanting to ask the lexer for the last
token it consumed (in particular, I'm interested in the
location_t of that token).  Is this a reasonable thing to add to the
lexer?

gcc/cp/ChangeLog:
	* call.c (build_new_op_1): Set the location of the result
	of cp_build_unary_op.
	* cp-tree.h (class cp_expr): New class.
	(perform_koenig_lookup): Convert return type and param from tree
	to cp_expr.
	(finish_increment_expr): Likewise.
	(finish_unary_op_expr): Likewise.
	(finish_id_expression): Likewise for return type.
	(build_class_member_access_expr): Likewise for param.
	(finish_class_member_access_expr): Likewise.
	(build_x_unary_op): Likewise.
	(build_c_cast): Likewise.
	(build_x_modify_expr): Likewise for return type.
	* name-lookup.c (lookup_arg_dependent_1): Likewise.
	(lookup_arg_dependent): Likewise; also for local "ret".
	* name-lookup.h (lookup_arg_dependent): Likewise for return type.
	* parser.c (struct cp_parser_expression_stack_entry): Likewise
	for field "lhs".
	(cp_parser_identifier): Likewise for return type.  Use cp_expr
	ctor to preserve the token's location.
	(cp_parser_string_literal): Likewise, building up a meaningful
	location for the case where a compound string literal is built by
	concatentation.
	(cp_parser_userdef_char_literal): Likewise for return type.
	(cp_parser_userdef_numeric_literal): Likewise.
	(cp_parser_fold_expression): Likewise.
	(cp_parser_primary_expression): Likewise, and for locals "expr",
	"lam", "id_expression", "decl".
	Use cp_expr ctor when parsing literals, to preserve the spelling
	location of the token.  Preserve the locations of parentheses.
	Preserve location when calling objc_lookup_ivar.
	(cp_parser_primary_expression): Convert return type from tree to
	cp_expr.
	(cp_parser_id_expression): Likewise.
	(cp_parser_unqualified_id): Likewise.  Also for local "id".
	(cp_parser_postfix_expression): Likewise, also for local
	"postfix_expression".  Preserve start location.  Use it
	to construct spelling ranges for C++-style casts.  Pass on the
	location of the closing parenthesis of a call site to
	cp_parser_parenthesized_expression_list, and use it to build
	a source range for a call.  Use cp_expr in ternary expression.
	(cp_parser_postfix_dot_deref_expression): Convert param from tree to
	cp_expr.
	(cp_parser_parenthesized_expression_list): Add "close_paren_loc"
	out-param, and write back to it.
	(cp_parser_unary_expression): Convert return type from tree to
	cp_expr.  Also for locals "cast_expression" and "expression".
	Generate suitable locations for cast expressions.
	Call protected_set_expr_location on expression.
	(cp_parser_new_expression): FIXME: unfinished attempt to
	generate meaningful locations (see note in blurb above).
	(cp_parser_cast_expression): Convert return type from tree to
	cp_expr; also for local "expr".  Use the paren location to generate a
	meaningful range for the expression.
	(cp_parser_binary_expression): Convert return type from tree to
	cp_expr; also for local "rhs".  Generate a meaningful location
	for the expression, and use it.  Replace call to
	protected_set_expr_location by converting a build2 to a
	build2_loc.
	(cp_parser_question_colon_clause): Convert return type from tree
	to cp_expr; also for local "assignment_expr".  Set the spelling
	range of the expression.
	(cp_parser_assignment_expression): Likewise for return type and
	locals "expr" and "rhs".  Build a meaningful spelling range for
	the expression, and call protected_set_expr_location since
	build_x_modify_expr appears not to always honor "loc".
	(cp_parser_expression): Likewise for return type and locals
	"expression" and "assignment_expression".  Build a meaningful
	spelling range for assignment expressions.
	(cp_parser_constant_expression): Likewise for return type and
	local "expression".
	(cp_parser_lambda_expression): Likewise for return type.
	(cp_parser_operator_function_id): Likewise.
	(cp_parser_operator): Likewise.  Generate a meaningful range,
	using cp_expr's ctor to return it.
	(cp_parser_initializer_clause): Likewise for local "initializer".
	(cp_parser_lookup_name): Likewise for return type.  Use cp_expr's
	ctor to preserve the location_t of the name.
	(cp_parser_simple_cast_expression): Likewise for return type.
	* semantics.c (perform_koenig_lookup): Likewise for return type
	and param.
	(finish_increment_expr): Likewise.  Generate a meaningful spelling
	range.
	(finish_unary_op_expr): Likewise.
	(finish_id_expression): Likewise.
	* typeck.c (build_class_member_access_expr): Likewise for param;
	generate range for result.
	(finish_class_member_access_expr): Likewise.
	(cp_build_binary_op): Convert a build2 to a build2_loc.
	(build_x_unary_op): Convert param from tree to cp_expr.
	(build_nop): Use the location of the input expression for the
	NOP_EXPR.
	(build_c_cast): Use the provided location_t for the result.
	Provide an overloaded variant that takes a cp_esxpr and returns
	a cp_expr.
	(build_x_modify_expr): Convert return type from tree to cp_expr.
	Call protected_set_expr_location on the result of
	cp_build_modify_expr.
	* typeck2.c (build_x_arrow): Call protected_set_expr_location on
	the result of cp_build_indirect_ref.

gcc/testsuite/ChangeLog:
	* g++.dg/plugin/diagnostic-test-expressions-1.c: New file.
	* g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c: New file.
	* g++.dg/plugin/plugin.exp (plugin_test_list): Add the above.

gcc/ChangeLog:
	* tree.c (get_pure_location): Make non-static.
	(set_source_range): Return the resulting location_t.
	(make_location): New function.
	* tree.h (get_pure_location): New decl.
	(set_source_range): Convert return type from void to location_t.
	(make_location): New decl.
---
 gcc/cp/call.c                                      |   6 +-
 gcc/cp/cp-tree.h                                   |  98 +++-
 gcc/cp/name-lookup.c                               |   6 +-
 gcc/cp/name-lookup.h                               |   2 +-
 gcc/cp/parser.c                                    | 328 +++++++++----
 gcc/cp/semantics.c                                 |  35 +-
 gcc/cp/typeck.c                                    |  60 ++-
 gcc/cp/typeck2.c                                   |   4 +-
 .../g++.dg/plugin/diagnostic-test-expressions-1.c  | 535 +++++++++++++++++++++
 .../diagnostic_plugin_test_tree_expression_range.c |  98 ++++
 gcc/testsuite/g++.dg/plugin/plugin.exp             |   5 +-
 gcc/tree.c                                         |  25 +-
 gcc/tree.h                                         |   8 +-
 13 files changed, 1053 insertions(+), 157 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
 create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 8cdda62..7794a96 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5768,7 +5768,11 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
     case REALPART_EXPR:
     case IMAGPART_EXPR:
     case ABS_EXPR:
-      return cp_build_unary_op (code, arg1, candidates != 0, complain);
+      {
+	tree result = cp_build_unary_op (code, arg1, candidates != 0, complain);
+	protected_set_expr_location (result, loc);
+	return result;
+      }
 
     case ARRAY_REF:
       return cp_build_array_ref (input_location, arg1, arg2, complain);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 84437b4..9d8069e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -40,6 +40,86 @@ c-common.h, not after.
 #include "c-family/c-common.h"
 #include "diagnostic.h"
 
+/* A tree node, together with a location, so that we can track locations
+   (and ranges) during parsing.
+
+   The location is redundant for node kinds that have locations,
+   but not all node kinds do (e.g. constants, and references to
+   params, locals, etc), so we stash a copy here.  */
+
+class cp_expr
+{
+public:
+  cp_expr () :
+    m_value (NULL), m_loc (UNKNOWN_LOCATION) {}
+
+  cp_expr (tree value) :
+    m_value (value), m_loc (EXPR_LOCATION (m_value))
+  {
+#if 0
+    /* FIXME: various assertions can be put in here when debugging,
+       for tracking down where location information gets thrown
+       away (during a trip through a purely "tree" value).  */
+    if (m_value && m_value != error_mark_node)
+      {
+	if (TREE_CODE (m_value) == FUNCTION_DECL)
+	  return; // for now
+	gcc_assert (CAN_HAVE_LOCATION_P (m_value));
+	//gcc_assert (m_loc != UNKNOWN_LOCATION);
+      }
+#endif
+  }
+
+  cp_expr (tree value, location_t loc):
+    m_value (value), m_loc (loc)
+  {
+    if (m_value)
+      gcc_assert (m_loc != UNKNOWN_LOCATION);
+  }
+
+  cp_expr (const cp_expr &other) :
+    m_value (other.m_value), m_loc (other.m_loc) {}
+
+  /* Implicit conversions to tree.  */
+  operator tree () const { return m_value; }
+  tree & operator* () { return m_value; }
+  tree & operator-> () { return m_value; }
+
+  tree get_value () const { return m_value; }
+  location_t get_location () const { return m_loc; }
+  location_t get_start () const
+  {
+    source_range src_range = get_range_from_loc (line_table, m_loc);
+    return src_range.m_start;
+  }
+  location_t get_finish () const
+  {
+    source_range src_range = get_range_from_loc (line_table, m_loc);
+    return src_range.m_finish;
+  }
+
+  void set_location (location_t loc)
+  {
+    protected_set_expr_location (m_value, loc);
+    m_loc = loc;
+  }
+
+  void set_range (location_t start, location_t finish)
+  {
+    m_loc = set_source_range (m_value, start, finish);
+  }
+
+ private:
+  tree m_value;
+  location_t m_loc;
+};
+
+inline bool
+operator == (const cp_expr &lhs, tree rhs)
+{
+  return lhs.get_value () == rhs;
+}
+
 #include "name-lookup.h"
 
 /* Usage of TREE_LANG_FLAG_?:
@@ -6292,15 +6372,15 @@ extern tree finish_stmt_expr_expr		(tree, tree);
 extern tree finish_stmt_expr			(tree, bool);
 extern tree stmt_expr_value_expr		(tree);
 bool empty_expr_stmt_p				(tree);
-extern tree perform_koenig_lookup		(tree, vec<tree, va_gc> *,
+extern cp_expr perform_koenig_lookup		(cp_expr, vec<tree, va_gc> *,
 						 tsubst_flags_t);
 extern tree finish_call_expr			(tree, vec<tree, va_gc> **, bool,
 						 bool, tsubst_flags_t);
 extern tree finish_template_variable		(tree, tsubst_flags_t = tf_warning_or_error);
-extern tree finish_increment_expr		(tree, enum tree_code);
+extern cp_expr finish_increment_expr		(cp_expr, enum tree_code);
 extern tree finish_this_expr			(void);
 extern tree finish_pseudo_destructor_expr       (tree, tree, tree, location_t);
-extern tree finish_unary_op_expr		(location_t, enum tree_code, tree,
+extern cp_expr finish_unary_op_expr		(location_t, enum tree_code, cp_expr,
 						 tsubst_flags_t);
 extern tree finish_compound_literal		(tree, tree, tsubst_flags_t);
 extern tree finish_fname			(tree);
@@ -6314,7 +6394,7 @@ extern tree finish_base_specifier		(tree, tree, bool);
 extern void finish_member_declaration		(tree);
 extern bool outer_automatic_var_p		(tree);
 extern tree process_outer_var_ref		(tree, tsubst_flags_t);
-extern tree finish_id_expression		(tree, tree, tree,
+extern cp_expr finish_id_expression		(tree, tree, tree,
 						 cp_id_kind *,
 						 bool, bool, bool *,
 						 bool, bool, bool, bool,
@@ -6556,9 +6636,9 @@ extern tree unlowered_expr_type                 (const_tree);
 extern tree decay_conversion			(tree,
                                                  tsubst_flags_t,
                                                  bool = true);
-extern tree build_class_member_access_expr      (tree, tree, tree, bool,
+extern tree build_class_member_access_expr      (cp_expr, tree, tree, bool,
 						 tsubst_flags_t);
-extern tree finish_class_member_access_expr     (tree, tree, bool, 
+extern tree finish_class_member_access_expr     (cp_expr, tree, bool,
 						 tsubst_flags_t);
 extern tree build_x_indirect_ref		(location_t, tree,
 						 ref_operator, tsubst_flags_t);
@@ -6580,7 +6660,7 @@ extern tree build_x_binary_op			(location_t,
 extern tree build_x_array_ref			(location_t, tree, tree,
 						 tsubst_flags_t);
 extern tree build_x_unary_op			(location_t,
-						 enum tree_code, tree,
+						 enum tree_code, cp_expr,
                                                  tsubst_flags_t);
 extern tree cp_build_addr_expr			(tree, tsubst_flags_t);
 extern tree cp_build_unary_op                   (enum tree_code, tree, int, 
@@ -6600,8 +6680,10 @@ extern tree build_static_cast			(tree, tree, tsubst_flags_t);
 extern tree build_reinterpret_cast		(tree, tree, tsubst_flags_t);
 extern tree build_const_cast			(tree, tree, tsubst_flags_t);
 extern tree build_c_cast			(location_t, tree, tree);
+extern cp_expr build_c_cast			(location_t loc, tree type,
+						 cp_expr expr);
 extern tree cp_build_c_cast			(tree, tree, tsubst_flags_t);
-extern tree build_x_modify_expr			(location_t, tree,
+extern cp_expr build_x_modify_expr		(location_t, tree,
 						 enum tree_code, tree,
 						 tsubst_flags_t);
 extern tree cp_build_modify_expr		(tree, enum tree_code, tree,
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index cebe57e..86c07ef 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -5659,7 +5659,7 @@ arg_assoc (struct arg_lookup *k, tree n)
 /* Performs Koenig lookup depending on arguments, where fns
    are the functions found in normal lookup.  */
 
-static tree
+static cp_expr
 lookup_arg_dependent_1 (tree name, tree fns, vec<tree, va_gc> *args)
 {
   struct arg_lookup k;
@@ -5720,10 +5720,10 @@ lookup_arg_dependent_1 (tree name, tree fns, vec<tree, va_gc> *args)
 
 /* Wrapper for lookup_arg_dependent_1.  */
 
-tree
+cp_expr
 lookup_arg_dependent (tree name, tree fns, vec<tree, va_gc> *args)
 {
-  tree ret;
+  cp_expr ret;
   bool subtime;
   subtime = timevar_cond_start (TV_NAME_LOOKUP);
   ret = lookup_arg_dependent_1 (name, fns, args);
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index d430edb..d2453e9 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -347,7 +347,7 @@ extern void do_toplevel_using_decl (tree, tree, tree);
 extern void do_local_using_decl (tree, tree, tree);
 extern tree do_class_using_decl (tree, tree);
 extern void do_using_directive (tree);
-extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
+extern cp_expr lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
 extern bool is_associated_namespace (tree, tree);
 extern void parse_using_directive (tree, tree);
 extern tree innermost_non_namespace_value (tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 5e8614b..6fcdb78 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -1785,7 +1785,7 @@ struct cp_parser_expression_stack_entry
 {
   /* Left hand side of the binary operation we are currently
      parsing.  */
-  tree lhs;
+  cp_expr lhs;
   /* Original tree code for left hand side, if it was a binary
      expression itself (used for -Wparentheses).  */
   enum tree_code lhs_type;
@@ -1939,15 +1939,15 @@ static cp_parser *cp_parser_new
 
 /* Lexical conventions [gram.lex]  */
 
-static tree cp_parser_identifier
+static cp_expr cp_parser_identifier
   (cp_parser *);
-static tree cp_parser_string_literal
+static cp_expr cp_parser_string_literal
   (cp_parser *, bool, bool, bool);
-static tree cp_parser_userdef_char_literal
+static cp_expr cp_parser_userdef_char_literal
   (cp_parser *);
 static tree cp_parser_userdef_string_literal
   (tree);
-static tree cp_parser_userdef_numeric_literal
+static cp_expr cp_parser_userdef_numeric_literal
   (cp_parser *);
 
 /* Basic concepts [gram.basic]  */
@@ -1957,11 +1957,11 @@ static bool cp_parser_translation_unit
 
 /* Expressions [gram.expr]  */
 
-static tree cp_parser_primary_expression
+static cp_expr cp_parser_primary_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
-static tree cp_parser_id_expression
+static cp_expr cp_parser_id_expression
   (cp_parser *, bool, bool, bool *, bool, bool);
-static tree cp_parser_unqualified_id
+static cp_expr cp_parser_unqualified_id
   (cp_parser *, bool, bool, bool, bool);
 static tree cp_parser_nested_name_specifier_opt
   (cp_parser *, bool, bool, bool, bool);
@@ -1969,19 +1969,19 @@ static tree cp_parser_nested_name_specifier
   (cp_parser *, bool, bool, bool, bool);
 static tree cp_parser_qualifying_entity
   (cp_parser *, bool, bool, bool, bool, bool);
-static tree cp_parser_postfix_expression
+static cp_expr cp_parser_postfix_expression
   (cp_parser *, bool, bool, bool, bool, cp_id_kind *);
 static tree cp_parser_postfix_open_square_expression
   (cp_parser *, tree, bool, bool);
 static tree cp_parser_postfix_dot_deref_expression
-  (cp_parser *, enum cpp_ttype, tree, bool, cp_id_kind *, location_t);
+  (cp_parser *, enum cpp_ttype, cp_expr, bool, cp_id_kind *, location_t);
 static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
-  (cp_parser *, int, bool, bool, bool *, bool = false);
+  (cp_parser *, int, bool, bool, bool *, bool = false, location_t * = NULL);
 /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
 enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
 static void cp_parser_pseudo_destructor_name
   (cp_parser *, tree, tree *, tree *);
-static tree cp_parser_unary_expression
+static cp_expr cp_parser_unary_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
 static enum tree_code cp_parser_unary_operator
   (cp_token *);
@@ -1999,23 +1999,23 @@ static vec<tree, va_gc> *cp_parser_new_initializer
   (cp_parser *);
 static tree cp_parser_delete_expression
   (cp_parser *);
-static tree cp_parser_cast_expression
+static cp_expr cp_parser_cast_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
-static tree cp_parser_binary_expression
+static cp_expr cp_parser_binary_expression
   (cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *);
 static tree cp_parser_question_colon_clause
-  (cp_parser *, tree);
-static tree cp_parser_assignment_expression
+  (cp_parser *, cp_expr);
+static cp_expr cp_parser_assignment_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
 static enum tree_code cp_parser_assignment_operator_opt
   (cp_parser *);
-static tree cp_parser_expression
+static cp_expr cp_parser_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
-static tree cp_parser_constant_expression
+static cp_expr cp_parser_constant_expression
   (cp_parser *, bool = false, bool * = NULL);
 static tree cp_parser_builtin_offsetof
   (cp_parser *);
-static tree cp_parser_lambda_expression
+static cp_expr cp_parser_lambda_expression
   (cp_parser *);
 static void cp_parser_lambda_introducer
   (cp_parser *, tree);
@@ -2170,7 +2170,7 @@ static void cp_parser_function_body
   (cp_parser *, bool);
 static tree cp_parser_initializer
   (cp_parser *, bool *, bool *);
-static tree cp_parser_initializer_clause
+static cp_expr cp_parser_initializer_clause
   (cp_parser *, bool *);
 static tree cp_parser_braced_list
   (cp_parser*, bool*);
@@ -2238,9 +2238,9 @@ static tree cp_parser_mem_initializer_id
 
 /* Overloading [gram.over] */
 
-static tree cp_parser_operator_function_id
+static cp_expr cp_parser_operator_function_id
   (cp_parser *);
-static tree cp_parser_operator
+static cp_expr cp_parser_operator
   (cp_parser *);
 
 /* Templates [gram.temp] */
@@ -2411,7 +2411,7 @@ static tree cp_parser_objc_struct_declaration
 
 /* Utility Routines */
 
-static tree cp_parser_lookup_name
+static cp_expr cp_parser_lookup_name
   (cp_parser *, tree, enum tag_types, bool, bool, bool, tree *, location_t);
 static tree cp_parser_lookup_name_simple
   (cp_parser *, tree, location_t);
@@ -2421,7 +2421,7 @@ static bool cp_parser_check_declarator_template_parameters
   (cp_parser *, cp_declarator *, location_t);
 static bool cp_parser_check_template_parameters
   (cp_parser *, unsigned, location_t, cp_declarator *);
-static tree cp_parser_simple_cast_expression
+static cp_expr cp_parser_simple_cast_expression
   (cp_parser *);
 static tree cp_parser_global_scope_opt
   (cp_parser *, bool);
@@ -3670,7 +3670,7 @@ cp_parser_pop_lexer (cp_parser *parser)
 /* Parse an identifier.  Returns an IDENTIFIER_NODE representing the
    identifier.  */
 
-static tree
+static cp_expr
 cp_parser_identifier (cp_parser* parser)
 {
   cp_token *token;
@@ -3678,7 +3678,10 @@ cp_parser_identifier (cp_parser* parser)
   /* Look for the identifier.  */
   token = cp_parser_require (parser, CPP_NAME, RT_NAME);
   /* Return the value.  */
-  return token ? token->u.value : error_mark_node;
+  if (token)
+    return cp_expr (token->u.value, token->location);
+  else
+    return error_mark_node;
 }
 
 /* Parse a sequence of adjacent string constants.  Returns a
@@ -3695,7 +3698,7 @@ cp_parser_identifier (cp_parser* parser)
    This code is largely lifted from lex_string() in c-lex.c.
 
    FUTURE: ObjC++ will need to handle @-strings here.  */
-static tree
+static cp_expr
 cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 			  bool lookup_udlit = true)
 {
@@ -3717,6 +3720,8 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
       return error_mark_node;
     }
 
+  location_t loc = tok->location;
+
   if (cpp_userdef_string_p (tok->type))
     {
       string_tree = USERDEF_LITERAL_VALUE (tok->u.value);
@@ -3754,11 +3759,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
     }
   else
     {
+      location_t last_tok_loc;
       gcc_obstack_init (&str_ob);
       count = 0;
 
       do
 	{
+	  last_tok_loc = tok->location;
 	  cp_lexer_consume_token (parser->lexer);
 	  count++;
 	  str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
@@ -3813,6 +3820,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 	}
       while (cp_parser_is_string_literal (tok));
 
+      /* A string literal built by concatenation has its caret=start at
+	 the start of the initial string, and its finish at the finish of
+	 the final string literal.  */
+      loc = make_location (loc, loc,
+			   get_range_from_loc (line_table,
+					       last_tok_loc).m_finish);
+
       strs = (cpp_string *) obstack_finish (&str_ob);
     }
 
@@ -3865,7 +3879,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
   if (count > 1)
     obstack_free (&str_ob, 0);
 
-  return value;
+  return cp_expr (value, loc);
 }
 
 /* Look up a literal operator with the name and the exact arguments.  */
@@ -3916,7 +3930,7 @@ lookup_literal_operator (tree name, vec<tree, va_gc> *args)
 /* Parse a user-defined char constant.  Returns a call to a user-defined
    literal operator taking the character as an argument.  */
 
-static tree
+static cp_expr
 cp_parser_userdef_char_literal (cp_parser *parser)
 {
   cp_token *token = cp_lexer_consume_token (parser->lexer);
@@ -4008,7 +4022,7 @@ make_string_pack (tree value)
 /* Parse a user-defined numeric constant.  returns a call to a user-defined
    literal operator.  */
 
-static tree
+static cp_expr
 cp_parser_userdef_numeric_literal (cp_parser *parser)
 {
   cp_token *token = cp_lexer_consume_token (parser->lexer);
@@ -4409,7 +4423,7 @@ cp_parser_fold_operator (cp_parser *parser)
 
    Note that the '(' and ')' are matched in primary expression. */
 
-static tree
+static cp_expr
 cp_parser_fold_expression (cp_parser *parser, tree expr1)
 {
   cp_id_kind pidk;
@@ -4529,7 +4543,7 @@ cp_parser_fold_expression (cp_parser *parser, tree expr1)
    Returns a representation of the expression.  Upon return, *IDK
    indicates what kind of id-expression (if any) was present.  */
 
-static tree
+static cp_expr
 cp_parser_primary_expression (cp_parser *parser,
 			      bool address_p,
 			      bool cast_p,
@@ -4614,7 +4628,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	  if (!cast_p)
 	    cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
 	}
-      return token->u.value;
+      return cp_expr (token->u.value, token->location);
 
     case CPP_CHAR_USERDEF:
     case CPP_CHAR16_USERDEF:
@@ -4672,9 +4686,11 @@ cp_parser_primary_expression (cp_parser *parser,
 	}
       /* Otherwise it's a normal parenthesized expression.  */
       {
-	tree expr;
+	cp_expr expr;
 	bool saved_greater_than_is_operator_p;
 
+	location_t open_paren_loc = token->location;
+
 	/* Consume the `('.  */
 	cp_lexer_consume_token (parser->lexer);
 	/* Within a parenthesized expression, a `>' token is always
@@ -4719,7 +4735,11 @@ cp_parser_primary_expression (cp_parser *parser,
 	   template-parameter-list now.  */
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
+
 	/* Consume the `)'.  */
+	token = cp_lexer_peek_token (parser->lexer);
+	location_t close_paren_loc = token->location;
+	expr.set_range (open_paren_loc, close_paren_loc);
 	if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
 	    && !cp_parser_uncommitted_to_tentative_parse_p (parser))
 	  cp_parser_skip_to_end_of_statement (parser);
@@ -4739,7 +4759,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	      return msg;
 	    /* ... else, fall though to see if it's a lambda.  */
 	  }
-	tree lam = cp_parser_lambda_expression (parser);
+	cp_expr lam = cp_parser_lambda_expression (parser);
 	/* Don't warn about a failed tentative parse.  */
 	if (cp_parser_error_occurred (parser))
 	  return error_mark_node;
@@ -4760,20 +4780,20 @@ cp_parser_primary_expression (cp_parser *parser,
 	  /* These two are the boolean literals.  */
 	case RID_TRUE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_true_node;
+	  return cp_expr (boolean_true_node, token->location);
 	case RID_FALSE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_false_node;
+	  return cp_expr (boolean_false_node, token->location);
 
 	  /* The `__null' literal.  */
 	case RID_NULL:
 	  cp_lexer_consume_token (parser->lexer);
-	  return null_node;
+	  return cp_expr (null_node, token->location);
 
 	  /* The `nullptr' literal.  */
 	case RID_NULLPTR:
 	  cp_lexer_consume_token (parser->lexer);
-	  return nullptr_node;
+	  return cp_expr (nullptr_node, token->location);
 
 	  /* Recognize the `this' keyword.  */
 	case RID_THIS:
@@ -4921,14 +4941,14 @@ cp_parser_primary_expression (cp_parser *parser,
     case CPP_TEMPLATE_ID:
     case CPP_NESTED_NAME_SPECIFIER:
       {
-	tree id_expression;
-	tree decl;
+      id_expression:
+	cp_expr id_expression;
+	cp_expr decl;
 	const char *error_msg;
 	bool template_p;
 	bool done;
 	cp_token *id_expr_token;
 
-      id_expression:
 	/* Parse the id-expression.  */
 	id_expression
 	  = cp_parser_id_expression (parser,
@@ -4997,8 +5017,20 @@ cp_parser_primary_expression (cp_parser *parser,
 	      }
 
 	    /* In Objective-C++, an instance variable (ivar) may be preferred
-	       to whatever cp_parser_lookup_name() found.  */
-	    decl = objc_lookup_ivar (decl, id_expression);
+	       to whatever cp_parser_lookup_name() found.
+	       Call objc_lookup_ivar.  To avoid exposing cp_expr to the
+	       rest of c-family, we have to do a little extra work to preserve
+	       any location information in cp_expr "decl".  Given that
+	       objc_lookup_ivar is implemented in "c-family" and "objc", we
+	       have a trip through the pure "tree" type, rather than cp_expr.
+	       Naively copying it back to "decl" would implicitly give the
+	       new cp_expr value an UNKNOWN_LOCATION for nodes that don't
+	       store an EXPR_LOCATION.  Hence we only update "decl" (and
+	       hence its location_t) if we get back a different tree node.  */
+	    tree decl_tree = objc_lookup_ivar (decl.get_value (),
+					       id_expression);
+	    if (decl_tree != decl.get_value ())
+	      decl = cp_expr (decl_tree);
 
 	    /* If name lookup gives us a SCOPE_REF, then the
 	       qualifying scope was dependent.  */
@@ -5039,7 +5071,7 @@ cp_parser_primary_expression (cp_parser *parser,
 		  {
 		    error_at (id_expr_token->location,
 			      "local variable %qD may not appear in this context",
-			      decl);
+			      decl.get_value ());
 		    return error_mark_node;
 		  }
 	      }
@@ -5067,7 +5099,7 @@ cp_parser_primary_expression (cp_parser *parser,
     }
 }
 
-static inline tree
+static inline cp_expr
 cp_parser_primary_expression (cp_parser *parser,
 			      bool address_p,
 			      bool cast_p,
@@ -5112,7 +5144,7 @@ cp_parser_primary_expression (cp_parser *parser,
    If DECLARATOR_P is true, the id-expression is appearing as part of
    a declarator, rather than as part of an expression.  */
 
-static tree
+static cp_expr
 cp_parser_id_expression (cp_parser *parser,
 			 bool template_keyword_p,
 			 bool check_dependency_p,
@@ -5247,7 +5279,7 @@ cp_parser_id_expression (cp_parser *parser,
    is true, the unqualified-id is appearing as part of a declarator,
    rather than as part of an expression.  */
 
-static tree
+static cp_expr
 cp_parser_unqualified_id (cp_parser* parser,
 			  bool template_keyword_p,
 			  bool check_dependency_p,
@@ -5510,7 +5542,7 @@ cp_parser_unqualified_id (cp_parser* parser,
     case CPP_KEYWORD:
       if (token->keyword == RID_OPERATOR)
 	{
-	  tree id;
+	  cp_expr id;
 
 	  /* This could be a template-id, so we try that first.  */
 	  cp_parser_parse_tentatively (parser);
@@ -6100,7 +6132,7 @@ cp_parser_compound_literal_p (cp_parser *parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
                               bool member_access_only_p, bool decltype_p,
 			      cp_id_kind * pidk_return)
@@ -6109,13 +6141,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
   location_t loc;
   enum rid keyword;
   cp_id_kind idk = CP_ID_KIND_NONE;
-  tree postfix_expression = NULL_TREE;
+  cp_expr postfix_expression = NULL_TREE;
   bool is_member_access = false;
   int saved_in_statement = -1;
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
   loc = token->location;
+  location_t start_loc = get_range_from_loc (line_table, loc).m_start;
+
   /* Some of the productions are determined by keywords.  */
   keyword = token->keyword;
   switch (keyword)
@@ -6126,7 +6160,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
     case RID_CONSTCAST:
       {
 	tree type;
-	tree expression;
+	cp_expr expression;
 	const char *saved_message;
 	bool saved_in_type_id_in_expr_p;
 
@@ -6159,7 +6193,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* And the expression which is being cast.  */
 	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
 	expression = cp_parser_expression (parser, & idk, /*cast_p=*/true);
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN,
+						   RT_CLOSE_PAREN);
+	location_t end_loc = close_paren ?
+	  close_paren->location : UNKNOWN_LOCATION;
 
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
@@ -6192,6 +6229,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  default:
 	    gcc_unreachable ();
 	  }
+
+	/* Construct a location e.g. :
+	     reinterpret_cast <int *> (expr)
+	     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	   ranging from the start of the "*_cast" token to the final closing
+	   paren, with the caret at the start.  */
+	location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc);
+	postfix_expression.set_location (cp_cast_loc);
       }
       break;
 
@@ -6471,6 +6516,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 							postfix_expression,
 							false,
 							decltype_p);
+          postfix_expression.set_range (start_loc,
+                                        postfix_expression.get_location ());
+
 	  idk = CP_ID_KIND_NONE;
           is_member_access = false;
 	  break;
@@ -6484,6 +6532,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    bool saved_non_integral_constant_expression_p = false;
 	    tsubst_flags_t complain = complain_flags (decltype_p);
 	    vec<tree, va_gc> *args;
+	    location_t close_paren_loc;
 
             is_member_access = false;
 
@@ -6503,7 +6552,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 		    (parser, non_attr,
 		     /*cast_p=*/false, /*allow_expansion_p=*/true,
 		     /*non_constant_p=*/NULL,
-		     /*want_literal_zero_p=*/warn_memset_transposed_args));
+		     /*want_literal_zero_p=*/warn_memset_transposed_args,
+		     /*close_paren_loc=*/&close_paren_loc));
 	    if (is_builtin_constant_p)
 	      {
 		parser->integral_constant_expression_p
@@ -6653,7 +6703,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 				    koenig_p,
 				    complain);
 
-	    protected_set_expr_location (postfix_expression, token->location);
+	    location_t combined_loc = make_location (token->location,
+						     start_loc,
+						     close_paren_loc);
+	    protected_set_expr_location (postfix_expression, combined_loc);
 
 	    /* The POSTFIX_EXPRESSION is certainly no longer an id.  */
 	    idk = CP_ID_KIND_NONE;
@@ -6714,7 +6767,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  if (pidk_return != NULL)
 	    * pidk_return = idk;
           if (member_access_only_p)
-            return is_member_access? postfix_expression : error_mark_node;
+            return is_member_access
+              ? postfix_expression
+              : cp_expr (error_mark_node);
           else
             return postfix_expression;
 	}
@@ -6914,7 +6969,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 static tree
 cp_parser_postfix_dot_deref_expression (cp_parser *parser,
 					enum cpp_ttype token_type,
-					tree postfix_expression,
+					cp_expr postfix_expression,
 					bool for_offsetof, cp_id_kind *idk,
 					location_t location)
 {
@@ -6951,7 +7006,7 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
       if (scope == unknown_type_node)
 	{
 	  error_at (location, "%qE does not have class type",
-		    postfix_expression);
+		    postfix_expression.get_value ());
 	  scope = NULL_TREE;
 	}
       /* Unlike the object expression in other contexts, *this is not
@@ -7124,7 +7179,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 bool cast_p,
                                          bool allow_expansion_p,
 					 bool *non_constant_p,
-					 bool want_literal_zero_p)
+					 bool want_literal_zero_p,
+					 location_t *close_paren_loc)
 {
   vec<tree, va_gc> *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
@@ -7271,6 +7327,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 	cp_lexer_consume_token (parser->lexer);
       }
 
+  if (close_paren_loc)
+    *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
     {
       int ending;
@@ -7440,7 +7499,7 @@ cp_parser_pseudo_destructor_name (cp_parser* parser,
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 			    bool address_p, bool cast_p, bool decltype_p)
 {
@@ -7654,8 +7713,8 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
     }
   if (unary_operator != ERROR_MARK)
     {
-      tree cast_expression;
-      tree expression = error_mark_node;
+      cp_expr cast_expression;
+      cp_expr expression = error_mark_node;
       non_integral_constant non_constant_p = NIC_NONE;
       location_t loc = token->location;
       tsubst_flags_t complain = complain_flags (decltype_p);
@@ -7669,6 +7728,14 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 				     /*cast_p=*/false,
 				     /*decltype*/false,
 				     pidk);
+
+      /* Make a location:
+	    OP_TOKEN  CAST_EXPRESSION
+	    ^~~~~~~~~~~~~~~~~~~~~~~~~
+	 with start==caret at the operator token, and
+	 extending to the end of the cast_expression.  */
+      loc = make_location (loc, loc, cast_expression.get_finish ());
+
       /* Now, build an appropriate representation.  */
       switch (unary_operator)
 	{
@@ -7704,6 +7771,8 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	  gcc_unreachable ();
 	}
 
+      protected_set_expr_location (expression, loc);
+
       if (non_constant_p != NIC_NONE
 	  && cp_parser_non_integral_constant_expression (parser,
 							 non_constant_p))
@@ -7767,6 +7836,8 @@ cp_parser_new_expression (cp_parser* parser)
   tree nelts = NULL_TREE;
   tree ret;
 
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   /* Look for the optional `::' operator.  */
   global_scope_p
     = (cp_parser_global_scope_opt (parser,
@@ -7861,6 +7932,11 @@ cp_parser_new_expression (cp_parser* parser)
   if (initializer != NULL)
     release_tree_vector (initializer);
 
+  /* FIXME: better location for result; currently just the location of
+     the initial token.
+     There doesn't seem to be an easy way to get at the last consumed
+     token from the lexer, which would give us the finish of the range.  */
+  protected_set_expr_location (ret, start_loc);
   return ret;
 }
 
@@ -8233,7 +8309,7 @@ cp_parser_tokens_start_cast_expression (cp_parser *parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 			   bool decltype_p, cp_id_kind * pidk)
 {
@@ -8241,7 +8317,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
       tree type = NULL_TREE;
-      tree expr = NULL_TREE;
+      cp_expr expr (NULL_TREE);
       int cast_expression = 0;
       const char *saved_message;
 
@@ -8254,7 +8330,9 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
       parser->type_definition_forbidden_message
 	= G_("types may not be defined in casts");
       /* Consume the `('.  */
-      cp_lexer_consume_token (parser->lexer);
+      cp_token *open_paren = cp_lexer_consume_token (parser->lexer);
+      location_t open_paren_loc = open_paren->location;
+
       /* A very tricky bit is that `(struct S) { 3 }' is a
 	 compound-literal (which we permit in C++ as an extension).
 	 But, that construct is not a cast-expression -- it is a
@@ -8356,7 +8434,15 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 		return error_mark_node;
 
 	      /* Perform the cast.  */
-	      expr = build_c_cast (input_location, type, expr);
+	      /* Make a location:
+		   (TYPE) EXPR
+		   ^~~~~~~~~~~
+		 with start==caret at the open paren, extending to the
+		 end of "expr".  */
+	      location_t cast_loc = make_location (open_paren_loc,
+						   open_paren_loc,
+						   expr.get_finish ());
+	      expr = build_c_cast (cast_loc, type, expr);
 	      return expr;
 	    }
 	}
@@ -8449,7 +8535,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
  ? PREC_NOT_OPERATOR					     \
  : binops_by_token[token->type].prec)
 
-static tree
+static cp_expr
 cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 			     bool no_toplevel_fold_p,
 			     bool decltype_p,
@@ -8459,7 +8545,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
   cp_parser_expression_stack stack;
   cp_parser_expression_stack_entry *sp = &stack[0];
   cp_parser_expression_stack_entry current;
-  tree rhs;
+  cp_expr rhs;
   cp_token *token;
   enum tree_code rhs_type;
   enum cp_parser_prec new_prec, lookahead_prec;
@@ -8599,6 +8685,11 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 				      maybe_constant_value (rhs));
 
       overload = NULL;
+
+      location_t combined_loc = make_location (current.loc,
+					       current.lhs.get_start (),
+					       rhs.get_finish ());
+
       /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type ==
 	 ERROR_MARK for everything that is not a binary expression.
 	 This makes warn_about_parentheses miss some warnings that
@@ -8609,18 +8700,18 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
       if (no_toplevel_fold_p
 	  && lookahead_prec <= current.prec
 	  && sp == stack)
-	current.lhs = build2 (current.tree_type,
-			      TREE_CODE_CLASS (current.tree_type)
-			      == tcc_comparison
-			      ? boolean_type_node : TREE_TYPE (current.lhs),
-			      current.lhs, rhs);
+	current.lhs = build2_loc (combined_loc,
+				  current.tree_type,
+				  TREE_CODE_CLASS (current.tree_type)
+				  == tcc_comparison
+				  ? boolean_type_node : TREE_TYPE (current.lhs),
+				  current.lhs, rhs);
       else
-	current.lhs = build_x_binary_op (current.loc, current.tree_type,
+	current.lhs = build_x_binary_op (combined_loc, current.tree_type,
 					 current.lhs, current.lhs_type,
 					 rhs, rhs_type, &overload,
 					 complain_flags (decltype_p));
       current.lhs_type = current.tree_type;
-      protected_set_expr_location (current.lhs, current.loc);
 
       /* If the binary operator required the use of an overloaded operator,
 	 then this expression cannot be an integral constant-expression.
@@ -8637,7 +8728,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
   return current.lhs;
 }
 
-static tree
+static cp_expr
 cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 			     bool no_toplevel_fold_p,
 			     enum cp_parser_prec prec,
@@ -8661,10 +8752,10 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
      ? : assignment-expression */
 
 static tree
-cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
+cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr)
 {
   tree expr, folded_logical_or_expr = cp_fully_fold (logical_or_expr);
-  tree assignment_expr;
+  cp_expr assignment_expr;
   struct cp_token *token;
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
@@ -8703,6 +8794,15 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
   c_inhibit_evaluation_warnings -=
     folded_logical_or_expr == truthvalue_true_node;
 
+  /* Make a location:
+       LOGICAL_OR_EXPR ? EXPR : ASSIGNMENT_EXPR
+       ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
+     with the caret at the "?", ranging from the start of
+     the logical_or_expr to the end of the assignment_expr.  */
+  loc = make_location (loc,
+		       logical_or_expr.get_start (),
+		       assignment_expr.get_finish ());
+
   /* Build the conditional-expression.  */
   return build_x_conditional_expr (loc, logical_or_expr,
 				   expr,
@@ -8722,11 +8822,11 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
 
    Returns a representation for the expression.  */
 
-static tree
+static cp_expr
 cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 				 bool cast_p, bool decltype_p)
 {
-  tree expr;
+  cp_expr expr;
 
   /* If the next token is the `throw' keyword, then we're looking at
      a throw-expression.  */
@@ -8758,7 +8858,8 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 	      location_t saved_input_location;
 
 	      /* Parse the right-hand side of the assignment.  */
-	      tree rhs = cp_parser_initializer_clause (parser, &non_constant_p);
+	      cp_expr rhs = cp_parser_initializer_clause (parser,
+							  &non_constant_p);
 
 	      if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
 		maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
@@ -8769,13 +8870,22 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 							      NIC_ASSIGNMENT))
 		return error_mark_node;
 	      /* Build the assignment expression.  Its default
-		 location is the location of the '=' token.  */
+		 location:
+		   LHS = RHS
+		   ~~~~^~~~~
+		 is the location of the '=' token as the
+		 caret, ranging from the start of the lhs to the
+		 end of the rhs.  */
 	      saved_input_location = input_location;
+	      loc = make_location (loc,
+				   expr.get_start (),
+				   rhs.get_finish ());
 	      input_location = loc;
 	      expr = build_x_modify_expr (loc, expr,
 					  assignment_operator,
 					  rhs,
 					  complain_flags (decltype_p));
+	      protected_set_expr_location (expr, loc);
 	      input_location = saved_input_location;
 	    }
 	}
@@ -8885,16 +8995,16 @@ cp_parser_assignment_operator_opt (cp_parser* parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
 		      bool cast_p, bool decltype_p)
 {
-  tree expression = NULL_TREE;
+  cp_expr expression = NULL_TREE;
   location_t loc = UNKNOWN_LOCATION;
 
   while (true)
     {
-      tree assignment_expression;
+      cp_expr assignment_expression;
 
       /* Parse the next assignment-expression.  */
       assignment_expression
@@ -8916,9 +9026,17 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
       if (!expression)
 	expression = assignment_expression;
       else
-	expression = build_x_compound_expr (loc, expression,
-					    assignment_expression,
-					    complain_flags (decltype_p));
+	{
+	  /* Create a location with caret at the comma, ranging
+	     from the start of the LHS to the end of the RHS.  */
+	  loc = make_location (loc,
+			       expression.get_start (),
+			       assignment_expression.get_finish ());
+	  expression = build_x_compound_expr (loc, expression,
+					      assignment_expression,
+					      complain_flags (decltype_p));
+	  expression.set_location (loc);
+	}
       /* If the next token is not a comma, or we're in a fold-expression, then
 	 we are done with the expression.  */
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)
@@ -8945,7 +9063,7 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
   constant, *NON_CONSTANT_P is set to TRUE.  If ALLOW_NON_CONSTANT_P
   is false, NON_CONSTANT_P should be NULL.  */
 
-static tree
+static cp_expr
 cp_parser_constant_expression (cp_parser* parser,
 			       bool allow_non_constant_p,
 			       bool *non_constant_p)
@@ -8953,7 +9071,7 @@ cp_parser_constant_expression (cp_parser* parser,
   bool saved_integral_constant_expression_p;
   bool saved_allow_non_integral_constant_expression_p;
   bool saved_non_integral_constant_expression_p;
-  tree expression;
+  cp_expr expression;
 
   /* It might seem that we could simply parse the
      conditional-expression, and then check to see if it were
@@ -9323,7 +9441,7 @@ finish_lambda_scope (void)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_lambda_expression (cp_parser* parser)
 {
   tree lambda_expr = build_lambda_expr ();
@@ -13325,7 +13443,7 @@ cp_parser_mem_initializer_id (cp_parser* parser)
    Returns an IDENTIFIER_NODE for the operator which is a
    human-readable spelling of the identifier, e.g., `operator +'.  */
 
-static tree
+static cp_expr
 cp_parser_operator_function_id (cp_parser* parser)
 {
   /* Look for the `operator' keyword.  */
@@ -13365,7 +13483,7 @@ cp_literal_operator_id (const char* name)
    Returns an IDENTIFIER_NODE for the operator which is a
    human-readable spelling of the identifier, e.g., `operator +'.  */
 
-static tree
+static cp_expr
 cp_parser_operator (cp_parser* parser)
 {
   tree id = NULL_TREE;
@@ -13374,6 +13492,9 @@ cp_parser_operator (cp_parser* parser)
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
+
+  location_t start_loc = token->location;
+
   /* Figure out which operator we have.  */
   switch (token->type)
     {
@@ -13390,7 +13511,7 @@ cp_parser_operator (cp_parser* parser)
 	  break;
 
 	/* Consume the `new' or `delete' token.  */
-	cp_lexer_consume_token (parser->lexer);
+	location_t end_loc = cp_lexer_consume_token (parser->lexer)->location;
 
 	/* Peek at the next token.  */
 	token = cp_lexer_peek_token (parser->lexer);
@@ -13401,7 +13522,8 @@ cp_parser_operator (cp_parser* parser)
 	    /* Consume the `[' token.  */
 	    cp_lexer_consume_token (parser->lexer);
 	    /* Look for the `]' token.  */
-	    cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
+	    end_loc = cp_parser_require (parser, CPP_CLOSE_SQUARE,
+                                         RT_CLOSE_SQUARE)->location;
 	    id = ansi_opname (op == NEW_EXPR
 			      ? VEC_NEW_EXPR : VEC_DELETE_EXPR);
 	  }
@@ -13409,7 +13531,9 @@ cp_parser_operator (cp_parser* parser)
 	else
 	  id = ansi_opname (op);
 
-	return id;
+	location_t loc = make_location (start_loc, start_loc, end_loc);
+
+	return cp_expr (id, loc);
       }
 
     case CPP_PLUS:
@@ -13655,7 +13779,7 @@ cp_parser_operator (cp_parser* parser)
       id = error_mark_node;
     }
 
-  return id;
+  return cp_expr (id, start_loc);
 }
 
 /* Parse a template-declaration.
@@ -20347,10 +20471,10 @@ cp_parser_initializer (cp_parser* parser, bool* is_direct_init,
 
    Otherwise, calls cp_parser_braced_list.  */
 
-static tree
+static cp_expr
 cp_parser_initializer_clause (cp_parser* parser, bool* non_constant_p)
 {
-  tree initializer;
+  cp_expr initializer;
 
   /* Assume the expression is constant.  */
   *non_constant_p = false;
@@ -24116,7 +24240,7 @@ cp_parser_nested_requirement (cp_parser *parser)
    TREE_LIST of candidates if name-lookup results in an ambiguity, and
    NULL_TREE otherwise.  */
 
-static tree
+static cp_expr
 cp_parser_lookup_name (cp_parser *parser, tree name,
 		       enum tag_types tag_type,
 		       bool is_template,
@@ -24358,7 +24482,7 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
 
   maybe_record_typedef_use (decl);
 
-  return decl;
+  return cp_expr (decl, name_location);
 }
 
 /* Like cp_parser_lookup_name, but for use in the typical case where
@@ -25362,7 +25486,7 @@ cp_parser_single_declaration (cp_parser* parser,
 
 /* Parse a cast-expression that is not the operand of a unary "&".  */
 
-static tree
+static cp_expr
 cp_parser_simple_cast_expression (cp_parser *parser)
 {
   return cp_parser_cast_expression (parser, /*address_p=*/false,
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index e7e5d8e..57d3458 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2164,8 +2164,8 @@ empty_expr_stmt_p (tree expr_stmt)
    the function (or functions) to call; ARGS are the arguments to the
    call.  Returns the functions to be considered by overload resolution.  */
 
-tree
-perform_koenig_lookup (tree fn, vec<tree, va_gc> *args,
+cp_expr
+perform_koenig_lookup (cp_expr fn, vec<tree, va_gc> *args,
 		       tsubst_flags_t complain)
 {
   tree identifier = NULL_TREE;
@@ -2460,10 +2460,15 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
    is indicated by CODE, which should be POSTINCREMENT_EXPR or
    POSTDECREMENT_EXPR.)  */
 
-tree
-finish_increment_expr (tree expr, enum tree_code code)
+cp_expr
+finish_increment_expr (cp_expr expr, enum tree_code code)
 {
-  return build_x_unary_op (input_location, code, expr, tf_warning_or_error);
+  /* input_location holds the location of the trailing operator token.  */
+  cp_expr result = build_x_unary_op (input_location, code, expr,
+				     tf_warning_or_error);
+  result.set_range (expr.get_start (),
+		    get_range_from_loc (line_table, input_location).m_finish);
+  return result;
 }
 
 /* Finish a use of `this'.  Returns an expression for `this'.  */
@@ -2557,15 +2562,17 @@ finish_pseudo_destructor_expr (tree object, tree scope, tree destructor,
 
 /* Finish an expression of the form CODE EXPR.  */
 
-tree
-finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
+cp_expr
+finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr,
 		      tsubst_flags_t complain)
 {
-  tree result = build_x_unary_op (loc, code, expr, complain);
+  location_t combined_loc = make_location (op_loc,
+					   op_loc, expr.get_finish ());
+  tree result = build_x_unary_op (combined_loc, code, expr, complain);
   tree result_ovl, expr_ovl;
 
   if (!(complain & tf_warning))
-    return result;
+    return cp_expr (result, combined_loc);
 
   result_ovl = result;
   expr_ovl = expr;
@@ -2575,15 +2582,15 @@ finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
 
   if (!CONSTANT_CLASS_P (expr_ovl)
       || TREE_OVERFLOW_P (expr_ovl))
-    return result;
+    return cp_expr (result, combined_loc);
 
   if (!processing_template_decl)
     result_ovl = cp_fully_fold (result_ovl);
 
   if (CONSTANT_CLASS_P (result_ovl) && TREE_OVERFLOW_P (result_ovl))
-    overflow_warning (input_location, result_ovl);
+    overflow_warning (combined_loc, result_ovl);
 
-  return result;
+  return cp_expr (result, combined_loc);
 }
 
 /* Finish a compound-literal expression.  TYPE is the type to which
@@ -3324,7 +3331,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
    the use of "this" explicit.
 
    Upon return, *IDK will be filled in appropriately.  */
-tree
+cp_expr
 finish_id_expression (tree id_expression,
 		      tree decl,
 		      tree scope,
@@ -3669,7 +3676,7 @@ finish_id_expression (tree id_expression,
 	}
     }
 
-  return decl;
+  return cp_expr (decl, location);
 }
 
 /* Implement the __typeof keyword: Return the type of EXPR, suitable for
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 6541e97..b6eff17 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2261,7 +2261,7 @@ lookup_anon_field (tree t, tree type)
    functions indicated by MEMBER.  */
 
 tree
-build_class_member_access_expr (tree object, tree member,
+build_class_member_access_expr (cp_expr object, tree member,
 				tree access_path, bool preserve_reference,
 				tsubst_flags_t complain)
 {
@@ -2291,10 +2291,10 @@ build_class_member_access_expr (tree object, tree member,
 	      && CLASS_TYPE_P (TREE_TYPE (object_type)))
 	    error ("request for member %qD in %qE, which is of pointer "
 		   "type %qT (maybe you meant to use %<->%> ?)",
-		   member, object, object_type);
+		   member, object.get_value (), object_type);
 	  else
 	    error ("request for member %qD in %qE, which is of non-class "
-		   "type %qT", member, object, object_type);
+		   "type %qT", member, object.get_value (), object_type);
 	}
       return error_mark_node;
     }
@@ -2435,7 +2435,12 @@ build_class_member_access_expr (tree object, tree member,
 	  member_type = cp_build_qualified_type (member_type, type_quals);
 	}
 
-      result = build3_loc (input_location, COMPONENT_REF, member_type,
+      location_t combined_loc =
+	make_location (input_location,
+		       object.get_start (),
+		       get_range_from_loc (line_table,
+					   input_location).m_finish);
+      result = build3_loc (combined_loc, COMPONENT_REF, member_type,
 			   object, member, NULL_TREE);
 
       /* Mark the expression const or volatile, as appropriate.  Even
@@ -2624,7 +2629,7 @@ check_template_keyword (tree decl)
    be a template via the use of the "A::template B" syntax.  */
 
 tree
-finish_class_member_access_expr (tree object, tree name, bool template_p,
+finish_class_member_access_expr (cp_expr object, tree name, bool template_p,
 				 tsubst_flags_t complain)
 {
   tree expr;
@@ -2658,7 +2663,7 @@ finish_class_member_access_expr (tree object, tree name, bool template_p,
 	      && TYPE_P (TREE_OPERAND (name, 0))
 	      && dependent_type_p (TREE_OPERAND (name, 0))))
 	return build_min_nt_loc (UNKNOWN_LOCATION, COMPONENT_REF,
-				 object, name, NULL_TREE);
+				 object.get_value (), name, NULL_TREE);
       object = build_non_dependent_expr (object);
     }
   else if (c_dialect_objc ()
@@ -2681,10 +2686,10 @@ finish_class_member_access_expr (tree object, tree name, bool template_p,
 	      && CLASS_TYPE_P (TREE_TYPE (object_type)))
 	    error ("request for member %qD in %qE, which is of pointer "
 		   "type %qT (maybe you meant to use %<->%> ?)",
-		   name, object, object_type);
+		   name, object.get_value (), object_type);
 	  else
 	    error ("request for member %qD in %qE, which is of non-class "
-		   "type %qT", name, object, object_type);
+		   "type %qT", name, object.get_value (), object_type);
 	}
       return error_mark_node;
     }
@@ -5102,7 +5107,7 @@ cp_build_binary_op (location_t location,
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
-  result = build2 (resultcode, build_type, op0, op1);
+  result = build2_loc (location, resultcode, build_type, op0, op1);
   if (final_type != 0)
     result = cp_convert (final_type, result, complain);
 
@@ -5260,7 +5265,7 @@ pointer_diff (tree op0, tree op1, tree ptrtype, tsubst_flags_t complain)
    and XARG is the operand.  */
 
 tree
-build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
+build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
 		  tsubst_flags_t complain)
 {
   tree orig_expr = xarg;
@@ -5270,7 +5275,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
   if (processing_template_decl)
     {
       if (type_dependent_expression_p (xarg))
-	return build_min_nt_loc (loc, code, xarg, NULL_TREE);
+	return build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE);
 
       xarg = build_non_dependent_expr (xarg);
     }
@@ -5305,7 +5310,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 		error (DECL_CONSTRUCTOR_P (fn)
 		       ? G_("taking address of constructor %qE")
 		       : G_("taking address of destructor %qE"),
-		       xarg);
+		       xarg.get_value ());
 	      return error_mark_node;
 	    }
 	}
@@ -5321,7 +5326,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 	      if (complain & tf_error)
 		{
 		  error ("invalid use of %qE to form a "
-			 "pointer-to-member-function", xarg);
+			 "pointer-to-member-function", xarg.get_value ());
 		  if (TREE_CODE (xarg) != OFFSET_REF)
 		    inform (input_location, "  a qualified-id is required");
 		}
@@ -5332,7 +5337,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 	      if (complain & tf_error)
 		error ("parentheses around %qE cannot be used to form a"
 		       " pointer-to-member-function",
-		       xarg);
+		       xarg.get_value ());
 	      else
 		return error_mark_node;
 	      PTRMEM_OK_P (xarg) = 1;
@@ -5429,7 +5434,7 @@ build_nop (tree type, tree expr)
 {
   if (type == error_mark_node || error_operand_p (expr))
     return expr;
-  return build1 (NOP_EXPR, type, expr);
+  return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr);
 }
 
 /* Take the address of ARG, whatever that means under C++ semantics.
@@ -7252,9 +7257,23 @@ build_const_cast (tree type, tree expr, tsubst_flags_t complain)
 /* Like cp_build_c_cast, but for the c-common bits.  */
 
 tree
-build_c_cast (location_t /*loc*/, tree type, tree expr)
+build_c_cast (location_t loc, tree type, tree expr)
 {
-  return cp_build_c_cast (type, expr, tf_warning_or_error);
+  tree result = cp_build_c_cast (type, expr, tf_warning_or_error);
+  protected_set_expr_location (result, loc);
+  return result;
+}
+
+/* Like the "build_c_cast" used for c-common, but using cp_expr to
+   preserve location information even for tree nodes that don't
+   support it.  */
+
+cp_expr
+build_c_cast (location_t loc, tree type, cp_expr expr)
+{
+  tree result = cp_build_c_cast (type, expr, tf_warning_or_error);
+  protected_set_expr_location (result, loc);
+  return cp_expr (result, loc);
 }
 
 /* Build an expression representing an explicit C-style cast to type
@@ -7762,7 +7781,7 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
   return result;
 }
 
-tree
+cp_expr
 build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 		     tree rhs, tsubst_flags_t complain)
 {
@@ -7782,7 +7801,10 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 	  return rval;
 	}
     }
-  return cp_build_modify_expr (lhs, modifycode, rhs, complain);
+
+  tree result = cp_build_modify_expr (lhs, modifycode, rhs, complain);
+  protected_set_expr_location (result, loc);
+  return result;
 }
 
 /* Helper function for get_delta_difference which assumes FROM is a base
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 839091c..6d58bda 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1740,7 +1740,9 @@ build_x_arrow (location_t loc, tree expr, tsubst_flags_t complain)
 	  return expr;
 	}
 
-      return cp_build_indirect_ref (last_rval, RO_NULL, complain);
+      tree result = cp_build_indirect_ref (last_rval, RO_NULL, complain);
+      protected_set_expr_location (result, loc);
+      return result;
     }
 
   if (complain & tf_error)
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
new file mode 100644
index 0000000..e34a66a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
@@ -0,0 +1,535 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret" } */
+
+/* This is a collection of unittests to verify that we're correctly
+   capturing the source code ranges of various kinds of expression.
+
+   It uses the various "diagnostic_test_*_expression_range_plugin"
+   plugins which handles "__emit_expression_range" by generating a warning
+   at the given source range of the input argument.  Each of the
+   different plugins do this at a different phase of the internal
+   representation (tree, gimple, etc), so we can verify that the
+   source code range information is valid at each phase.
+
+   We want to accept an expression of any type.  To do this in C, we
+   use variadic arguments, but C requires at least one argument before
+   the ellipsis, so we have a dummy one.  */
+
+extern void __emit_expression_range (int dummy, ...);
+
+int global;
+
+void test_parentheses (int a, int b)
+{
+  __emit_expression_range (0, (a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) );
+                               ~~~^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, (a + b) * (a - b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) * (a - b) );
+                               ~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, !(a && b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !(a && b) );
+                               ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Postfix expressions.  ************************************************/
+
+void test_array_reference (int *arr)
+{
+  __emit_expression_range (0, arr[100] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, arr[100] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+}
+
+int test_function_call (int p, int q, int r)
+{
+  __emit_expression_range (0, test_function_call (p, q, r) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, test_function_call (p, q, r) );
+                               ~~~~~~~~~~~~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+  return 0;
+}
+
+struct test_struct
+{
+  int field;
+};
+
+int test_structure_references (struct test_struct *ptr)
+{
+  struct test_struct local;
+  local.field = 42;
+
+  __emit_expression_range (0, local.field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, local.field );
+                               ~~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, ptr->field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ptr->field );
+                               ~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+int test_postfix_incdec (int i)
+{
+  __emit_expression_range (0, i++ ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i++ );
+                               ~^~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, i-- ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i-- );
+                               ~^~
+   { dg-end-multiline-output "" } */
+}
+
+/* Unary operators.  ****************************************************/
+
+int test_prefix_incdec (int i)
+{
+  __emit_expression_range (0, ++i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ++i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, --i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, --i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_address_operator (void)
+{
+  __emit_expression_range (0, &global ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &global );
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_indirection (int *ptr)
+{
+  __emit_expression_range (0, *ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *ptr );
+                               ^~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_unary_minus (int i)
+{
+  __emit_expression_range (0, -i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, -i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_ones_complement (int i)
+{
+  __emit_expression_range (0, ~i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ~i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_negation (int flag)
+{
+  __emit_expression_range (0, !flag ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !flag );
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Casts.  ****************************************************/
+
+void test_cast (void *ptr)
+{
+  __emit_expression_range (0, (int *)ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (int *)ptr );
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, *(int *)0xdeadbeef ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *(int *)0xdeadbeef );
+                               ^~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* Binary operators.  *******************************************/
+
+void test_multiplicative_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs * rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs * rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs / rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs / rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs % rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs % rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_additive_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs + rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs + rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs - rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs - rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_shift_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs << rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs << rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >> rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >> rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_relational_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs < rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs < rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs > rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs > rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs <= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs <= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_equality_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs == rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs == rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs != rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs != rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_bitwise_binary_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs & rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs & rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs ^ rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs ^ rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs | rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs | rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs && rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs && rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs || rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs || rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Conditional operator.  *******************************************/
+
+void test_conditional_operators (int flag, int on_true, int on_false)
+{
+  __emit_expression_range (0, flag ? on_true : on_false ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, flag ? on_true : on_false );
+                               ~~~~~^~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Assignment expressions.  *******************************************/
+
+void test_assignment_expressions (int dest, int other)
+{
+  __emit_expression_range (0, dest = other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest = other );
+                               ~~~~~^~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest *= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest *= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest /= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest /= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest %= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest %= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest += other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest += other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest -= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest -= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest <<= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest <<= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest >>= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest >>= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest &= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest &= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest ^= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest ^= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest |= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest |= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Comma operator.  *******************************************/
+
+void test_comma_operator (int a, int b)
+{
+  __emit_expression_range (0, (a++, a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a++, a + b) );
+                               ~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Literals.  **************************************************/
+
+/* We can't test the ranges of literals directly, since the underlying
+   tree nodes don't retain a location.  However, we can test that they
+   have ranges during parsing by building compound expressions using
+   them, and verifying the ranges of the compound expressions.  */
+
+void test_string_literals (int i)
+{
+  __emit_expression_range (0, "foo"[i] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, "foo"[i] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, &"foo" "bar" ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &"foo" "bar" );
+                               ^~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Examples of non-trivial expressions.  ****************************/
+
+extern double sqrt (double x);
+
+void test_quadratic (double a, double b, double c)
+{
+  __emit_expression_range (0, b * b - 4 * a * c ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, b * b - 4 * a * c );
+                               ~~~~~~^~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0,
+     (-b + sqrt (b * b - 4 * a * c))
+     / (2 * a)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+      (-b + sqrt (b * b - 4 * a * c))
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      / (2 * a));
+      ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* C++-specific expresssions. ****************************************/
+
+void test_cp_literal_keywords (int a, int b)
+{
+  this; /* { dg-error "invalid use of 'this' in non-member function" } */
+/* { dg-begin-multiline-output "" }
+   this;
+   ^~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+class base {
+ public:
+  base ();
+  base (int i);
+  virtual ~base ();
+};
+class derived : public base { ~derived (); };
+
+void test_cp_casts (base *ptr)
+{
+  __emit_expression_range (0, dynamic_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dynamic_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, static_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, static_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, reinterpret_cast <int *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, reinterpret_cast <int *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, const_cast <base *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, const_cast <base *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_new (void)
+{
+  /* TODO: currently these only cover the first token; see the FIXME in
+     cp_parser_new_expression.  */
+
+  __emit_expression_range (0, ::new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ::new base);
+                               ^~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base);
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base));
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base (42));
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base) (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base) (42));
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  /* TODO: placement new.  */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
new file mode 100644
index 0000000..89cc95a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
@@ -0,0 +1,98 @@
+/* This plugin verifies the source-code location ranges of
+   expressions, at the pre-gimplification tree stage.  */
+/* { dg-options "-O" } */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "print-tree.h"
+
+int plugin_is_GPL_compatible;
+
+static void
+emit_warning (location_t loc)
+{
+  source_range src_range = get_range_from_loc (line_table, loc);
+  warning_at (loc, 0,
+	      "tree range %i:%i-%i:%i",
+	      LOCATION_LINE (src_range.m_start),
+	      LOCATION_COLUMN (src_range.m_start),
+	      LOCATION_LINE (src_range.m_finish),
+	      LOCATION_COLUMN (src_range.m_finish));
+}
+
+tree
+cb_walk_tree_fn (tree * tp, int * walk_subtrees,
+		 void * data ATTRIBUTE_UNUSED)
+{
+  if (TREE_CODE (*tp) != CALL_EXPR)
+    return NULL_TREE;
+
+  tree call_expr = *tp;
+  tree fn = CALL_EXPR_FN (call_expr);
+  if (TREE_CODE (fn) != ADDR_EXPR)
+    return NULL_TREE;
+  fn = TREE_OPERAND (fn, 0);
+  if (TREE_CODE (fn) != FUNCTION_DECL)
+    return NULL_TREE;
+  if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fn)), "__emit_expression_range"))
+    return NULL_TREE;
+
+  /* Get arg 1; print it! */
+  tree arg = CALL_EXPR_ARG (call_expr, 1);
+
+  emit_warning (EXPR_LOCATION (arg));
+
+  return NULL_TREE;
+}
+
+static void
+callback (void *gcc_data, void *user_data)
+{
+  tree fndecl = (tree)gcc_data;
+  walk_tree (&DECL_SAVED_TREE (fndecl), cb_walk_tree_fn, NULL, NULL);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  register_callback (plugin_name,
+		     PLUGIN_PRE_GENERICIZE,
+		     callback,
+		     NULL);
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
index 3ed1397..2266380 100644
--- a/gcc/testsuite/g++.dg/plugin/plugin.exp
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -62,7 +62,10 @@ set plugin_test_list [list \
     { dumb_plugin.c dumb-plugin-test-1.C } \
     { header_plugin.c header-plugin-test.C } \
     { decl_plugin.c decl-plugin-test.C } \
-    { def_plugin.c def-plugin-test.C } ]
+    { def_plugin.c def-plugin-test.C } \
+    { diagnostic_plugin_test_tree_expression_range.c \
+	  diagnostic-test-expressions-1.c } \
+]
 
 foreach plugin_test $plugin_test_list {
     # Replace each source file with its full-path name
diff --git a/gcc/tree.c b/gcc/tree.c
index 1d770c3..6b7caa3 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13813,7 +13813,7 @@ nonnull_arg_p (const_tree arg)
 /* Given location LOC, strip away any packed range information
    or ad-hoc information.  */
 
-static location_t
+location_t
 get_pure_location (location_t loc)
 {
   if (IS_ADHOC_LOC (loc))
@@ -13843,20 +13843,20 @@ set_block (location_t loc, tree block)
   return COMBINE_LOCATION_DATA (line_table, pure_loc, src_range, block);
 }
 
-void
+location_t
 set_source_range (tree expr, location_t start, location_t finish)
 {
   source_range src_range;
   src_range.m_start = start;
   src_range.m_finish = finish;
-  set_source_range (expr, src_range);
+  return set_source_range (expr, src_range);
 }
 
-void
+location_t
 set_source_range (tree expr, source_range src_range)
 {
   if (!EXPR_P (expr))
-    return;
+    return UNKNOWN_LOCATION;
 
   location_t pure_loc = get_pure_location (EXPR_LOCATION (expr));
   location_t adhoc = COMBINE_LOCATION_DATA (line_table,
@@ -13864,6 +13864,21 @@ set_source_range (tree expr, source_range src_range)
 					    src_range,
 					    NULL);
   SET_EXPR_LOCATION (expr, adhoc);
+  return adhoc;
+}
+
+location_t
+make_location (location_t caret, location_t start, location_t finish)
+{
+  location_t pure_loc = get_pure_location (caret);
+  source_range src_range;
+  src_range.m_start = start;
+  src_range.m_finish = finish;
+  location_t combined_loc = COMBINE_LOCATION_DATA (line_table,
+						   pure_loc,
+						   src_range,
+						   NULL);
+  return combined_loc;
 }
 
 #include "gt-tree.h"
diff --git a/gcc/tree.h b/gcc/tree.h
index 0b9c3b9..dfcf540 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5295,6 +5295,7 @@ type_with_alias_set_p (const_tree t)
   return false;
 }
 
+extern location_t get_pure_location (location_t loc);
 extern location_t set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
@@ -5303,10 +5304,10 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
 
 extern bool nonnull_arg_p (const_tree);
 
-extern void
+extern location_t
 set_source_range (tree expr, location_t start, location_t finish);
 
-extern void
+extern location_t
 set_source_range (tree expr, source_range src_range);
 
 static inline source_range
@@ -5316,4 +5317,7 @@ get_decl_source_range (tree decl)
   return get_range_from_loc (line_table, loc);
 }
 
+extern location_t
+make_location (location_t caret, location_t start, location_t finish);
+
 #endif  /* GCC_TREE_H  */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-15  4:43 ` [PATCH/RFC] C++ FE: expression ranges (v2) David Malcolm
@ 2015-11-19 20:46   ` Jason Merrill
  2015-11-21  8:22     ` Jason Merrill
  0 siblings, 1 reply; 40+ messages in thread
From: Jason Merrill @ 2015-11-19 20:46 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 11/15/2015 12:01 AM, David Malcolm wrote:
> As with the C frontend, there's an issue with tree nodes that
> don't have locations: VAR_DECL, INTEGER_CST, etc:
>
>    int test (int foo)
>    {
>      return foo * 100;
>             ^^^   ^^^
>    }
>
> where we'd like to access the source spelling ranges of the expressions
> during parsing, so that we can use them when reporting parser errors.

Hmm, I had been thinking to address this in the C++ front end by 
wrapping uses in another tree: NOP_EXPR for rvalues, VIEW_CONVERT_EXPR 
for lvalues.

Jason

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-21  8:22     ` Jason Merrill
@ 2015-11-21  8:22       ` Jakub Jelinek
  2015-11-23 10:02         ` Richard Biener
  2015-11-25 20:32       ` [PATCH/RFC 0/2] C++ FE: expression ranges (v3) David Malcolm
  1 sibling, 1 reply; 40+ messages in thread
From: Jakub Jelinek @ 2015-11-21  8:22 UTC (permalink / raw)
  To: Jason Merrill; +Cc: David Malcolm, gcc-patches

On Sat, Nov 21, 2015 at 02:16:49AM -0500, Jason Merrill wrote:
> On 11/19/2015 03:46 PM, Jason Merrill wrote:
> >On 11/15/2015 12:01 AM, David Malcolm wrote:
> >>As with the C frontend, there's an issue with tree nodes that
> >>don't have locations: VAR_DECL, INTEGER_CST, etc:
> >>
> >>   int test (int foo)
> >>   {
> >>     return foo * 100;
> >>            ^^^   ^^^
> >>   }
> >>
> >>where we'd like to access the source spelling ranges of the expressions
> >>during parsing, so that we can use them when reporting parser errors.
> >
> >Hmm, I had been thinking to address this in the C++ front end by
> >wrapping uses in another tree: NOP_EXPR for rvalues, VIEW_CONVERT_EXPR
> >for lvalues.
> 
> On the other hand, my direction seems likely to cause more issues,
> especially with code that doesn't yet know how to handle VIEW_CONVERT_EXPR,
> and could create ambiguity with explicit conversions.  So I guess your
> approach seems reasonable.

But your approach would allow better diagnostics even in places where you
don't have the structures with tree, location_t pairs around.  With that
it will be limited solely to the parser and nothing else, so even template
instantiation if it is something that can be only detected when
instantiating would be too late.

I think using a new tree (rather than using NOP_EXPR/VIEW_CONVERT_EXPR)
that would be just some expression with location and teaching the FE and
folder about it might be even better.

	Jakub

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-19 20:46   ` Jason Merrill
@ 2015-11-21  8:22     ` Jason Merrill
  2015-11-21  8:22       ` Jakub Jelinek
  2015-11-25 20:32       ` [PATCH/RFC 0/2] C++ FE: expression ranges (v3) David Malcolm
  0 siblings, 2 replies; 40+ messages in thread
From: Jason Merrill @ 2015-11-21  8:22 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 11/19/2015 03:46 PM, Jason Merrill wrote:
> On 11/15/2015 12:01 AM, David Malcolm wrote:
>> As with the C frontend, there's an issue with tree nodes that
>> don't have locations: VAR_DECL, INTEGER_CST, etc:
>>
>>    int test (int foo)
>>    {
>>      return foo * 100;
>>             ^^^   ^^^
>>    }
>>
>> where we'd like to access the source spelling ranges of the expressions
>> during parsing, so that we can use them when reporting parser errors.
>
> Hmm, I had been thinking to address this in the C++ front end by
> wrapping uses in another tree: NOP_EXPR for rvalues, VIEW_CONVERT_EXPR
> for lvalues.

On the other hand, my direction seems likely to cause more issues, 
especially with code that doesn't yet know how to handle 
VIEW_CONVERT_EXPR, and could create ambiguity with explicit conversions. 
  So I guess your approach seems reasonable.

What is the memory consumption impact of this change?

> Also, in cp_parser_new_expression I attempted to generate meaningful
> ranges e.g.:
>
>   int *foo = new int[100];
>              ^~~~~~~~~~~~
>
> but it seems to be hard to do this, due to the trailing optional
> components; I found myself wanting to ask the lexer for the last
> token it consumed (in particular, I'm interested in the
> location_t of that token).  Is this a reasonable thing to add to the
> lexer?

cp_lexer_previous_token seems like what you want.

> -      return cp_build_unary_op (code, arg1, candidates != 0, complain);
> +      {
> +	tree result = cp_build_unary_op (code, arg1, candidates != 0, complain);
> +	protected_set_expr_location (result, loc);

I'd much rather pass loc into cp_build_unary_op.  At least add a FIXME 
to that effect.  Likewise in the other places you call *build* and then 
set the loc.

> +#if 0
> +    /* FIXME: various assertions can be put in here when debugging,
> +       for tracking down where location information gets thrown
> +       away (during a trip through a purely "tree" value).  */
> +    if (m_value && m_value != error_mark_node)
> +      {
> +	if (TREE_CODE (m_value) == FUNCTION_DECL)
> +	  return; // for now
> +	gcc_assert (CAN_HAVE_LOCATION_P (m_value));
> +	//gcc_assert (m_loc != UNKNOWN_LOCATION);

Yeah, I don't think you want to assert on UNKNOWN_LOCATION; some code 
does not and should not have an associated location.  In particular, 
cleanups.

>  Build the assignment expression.  Its default
> -		 location is the location of the '=' token.  */
> +		 location:
> +		   LHS = RHS
> +		   ~~~~^~~~~
> +		 is the location of the '=' token as the
> +		 caret, ranging from the start of the lhs to the
> +		 end of the rhs.  */
>  	      saved_input_location = input_location;
> +	      loc = make_location (loc,
> +				   expr.get_start (),
> +				   rhs.get_finish ());
>  	      input_location = loc;
>  	      expr = build_x_modify_expr (loc, expr,
>  					  assignment_operator,
>  					  rhs,
>  					  complain_flags (decltype_p));
> +	      protected_set_expr_location (expr, loc);
>  	      input_location = saved_input_location;

Do we still need to mess with input_location here?  If so, please add a 
FIXME explaining what still needs to be fixed.

Jason

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-21  8:22       ` Jakub Jelinek
@ 2015-11-23 10:02         ` Richard Biener
  2015-11-23 16:58           ` David Malcolm
  0 siblings, 1 reply; 40+ messages in thread
From: Richard Biener @ 2015-11-23 10:02 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Jason Merrill, David Malcolm, GCC Patches

On Sat, Nov 21, 2015 at 9:21 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Sat, Nov 21, 2015 at 02:16:49AM -0500, Jason Merrill wrote:
>> On 11/19/2015 03:46 PM, Jason Merrill wrote:
>> >On 11/15/2015 12:01 AM, David Malcolm wrote:
>> >>As with the C frontend, there's an issue with tree nodes that
>> >>don't have locations: VAR_DECL, INTEGER_CST, etc:
>> >>
>> >>   int test (int foo)
>> >>   {
>> >>     return foo * 100;
>> >>            ^^^   ^^^
>> >>   }
>> >>
>> >>where we'd like to access the source spelling ranges of the expressions
>> >>during parsing, so that we can use them when reporting parser errors.
>> >
>> >Hmm, I had been thinking to address this in the C++ front end by
>> >wrapping uses in another tree: NOP_EXPR for rvalues, VIEW_CONVERT_EXPR
>> >for lvalues.
>>
>> On the other hand, my direction seems likely to cause more issues,
>> especially with code that doesn't yet know how to handle VIEW_CONVERT_EXPR,
>> and could create ambiguity with explicit conversions.  So I guess your
>> approach seems reasonable.
>
> But your approach would allow better diagnostics even in places where you
> don't have the structures with tree, location_t pairs around.  With that
> it will be limited solely to the parser and nothing else, so even template
> instantiation if it is something that can be only detected when
> instantiating would be too late.
>
> I think using a new tree (rather than using NOP_EXPR/VIEW_CONVERT_EXPR)
> that would be just some expression with location and teaching the FE and
> folder about it might be even better.

Agreed.  Note that we already have NON_LVALUE_EXPR and fold-const.c uses
that to stick locations on things that cannot have them.

OTOH I would like to get rid of NON_LVALUE_EXPR in the middle-end (and thus
fold-const.c).

Richard.

>         Jakub

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-23 10:02         ` Richard Biener
@ 2015-11-23 16:58           ` David Malcolm
  2015-11-23 17:08             ` Jakub Jelinek
  0 siblings, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-11-23 16:58 UTC (permalink / raw)
  To: Richard Biener; +Cc: Jakub Jelinek, Jason Merrill, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 2210 bytes --]

On Mon, 2015-11-23 at 10:59 +0100, Richard Biener wrote:
> On Sat, Nov 21, 2015 at 9:21 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> > On Sat, Nov 21, 2015 at 02:16:49AM -0500, Jason Merrill wrote:
> >> On 11/19/2015 03:46 PM, Jason Merrill wrote:
> >> >On 11/15/2015 12:01 AM, David Malcolm wrote:
> >> >>As with the C frontend, there's an issue with tree nodes that
> >> >>don't have locations: VAR_DECL, INTEGER_CST, etc:
> >> >>
> >> >>   int test (int foo)
> >> >>   {
> >> >>     return foo * 100;
> >> >>            ^^^   ^^^
> >> >>   }
> >> >>
> >> >>where we'd like to access the source spelling ranges of the expressions
> >> >>during parsing, so that we can use them when reporting parser errors.
> >> >
> >> >Hmm, I had been thinking to address this in the C++ front end by
> >> >wrapping uses in another tree: NOP_EXPR for rvalues, VIEW_CONVERT_EXPR
> >> >for lvalues.
> >>
> >> On the other hand, my direction seems likely to cause more issues,
> >> especially with code that doesn't yet know how to handle VIEW_CONVERT_EXPR,
> >> and could create ambiguity with explicit conversions.  So I guess your
> >> approach seems reasonable.
> >
> > But your approach would allow better diagnostics even in places where you
> > don't have the structures with tree, location_t pairs around.  With that
> > it will be limited solely to the parser and nothing else, so even template
> > instantiation if it is something that can be only detected when
> > instantiating would be too late.
> >
> > I think using a new tree (rather than using NOP_EXPR/VIEW_CONVERT_EXPR)
> > that would be just some expression with location and teaching the FE and
> > folder about it might be even better.
> 
> Agreed.  Note that we already have NON_LVALUE_EXPR and fold-const.c uses
> that to stick locations on things that cannot have them.
> 
> OTOH I would like to get rid of NON_LVALUE_EXPR in the middle-end (and thus
> fold-const.c).

Thanks.

Does the following look like the kind of thing you had in mind?  (just
the tree.def part for now).   Presumably usable for both lvalues and
rvalues, where the thing it wraps is what's important.  It merely exists
to add an EXPR_LOCATION, for a usage of the wrapped thing.


[-- Attachment #2: tree.def.patch --]
[-- Type: text/x-patch, Size: 726 bytes --]

diff --git a/gcc/tree.def b/gcc/tree.def
index 44e5a5e..30fd766 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1407,6 +1407,13 @@ DEFTREECODE (CILK_SPAWN_STMT, "cilk_spawn_stmt", tcc_statement, 1)
 /* Cilk Sync statement: Does not have any operands.  */
 DEFTREECODE (CILK_SYNC_STMT, "cilk_sync_stmt", tcc_statement, 0)
 
+/* Wrapper to add a source code location to an expression, either one
+   that doesn't have one (such as an INTEGER_CST), or to a usage of a
+   variable (e.g. PARAM_DECL or VAR_DECL), where we want to record
+   the site in the source where the variable was *used* rather than
+   where it was declared.  */
+DEFTREECODE (LOCATION_EXPR, "location_expr", tcc_unary, 1)
+
 /*
 Local variables:
 mode:c

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-23 16:58           ` David Malcolm
@ 2015-11-23 17:08             ` Jakub Jelinek
  2015-11-23 17:09               ` Marek Polacek
  0 siblings, 1 reply; 40+ messages in thread
From: Jakub Jelinek @ 2015-11-23 17:08 UTC (permalink / raw)
  To: David Malcolm; +Cc: Richard Biener, Jason Merrill, GCC Patches

On Mon, Nov 23, 2015 at 11:53:40AM -0500, David Malcolm wrote:
> Does the following look like the kind of thing you had in mind?  (just
> the tree.def part for now).   Presumably usable for both lvalues and
> rvalues, where the thing it wraps is what's important.  It merely exists
> to add an EXPR_LOCATION, for a usage of the wrapped thing.

Yes, but please see with Jason, Richard and perhaps others if they are ok
with that too before spending too much time in that direction.
All occurrences of it would have to be folded away during the gimplification
at latest, this shouldn't be something we use in the middle-end.

> diff --git a/gcc/tree.def b/gcc/tree.def
> index 44e5a5e..30fd766 100644
> --- a/gcc/tree.def
> +++ b/gcc/tree.def
> @@ -1407,6 +1407,13 @@ DEFTREECODE (CILK_SPAWN_STMT, "cilk_spawn_stmt", tcc_statement, 1)
>  /* Cilk Sync statement: Does not have any operands.  */
>  DEFTREECODE (CILK_SYNC_STMT, "cilk_sync_stmt", tcc_statement, 0)
>  
> +/* Wrapper to add a source code location to an expression, either one
> +   that doesn't have one (such as an INTEGER_CST), or to a usage of a
> +   variable (e.g. PARAM_DECL or VAR_DECL), where we want to record
> +   the site in the source where the variable was *used* rather than
> +   where it was declared.  */
> +DEFTREECODE (LOCATION_EXPR, "location_expr", tcc_unary, 1)
> +
>  /*
>  Local variables:
>  mode:c

	Jakub

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-23 17:08             ` Jakub Jelinek
@ 2015-11-23 17:09               ` Marek Polacek
  2015-11-23 19:45                 ` Jason Merrill
  0 siblings, 1 reply; 40+ messages in thread
From: Marek Polacek @ 2015-11-23 17:09 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: David Malcolm, Richard Biener, Jason Merrill, GCC Patches

On Mon, Nov 23, 2015 at 05:57:54PM +0100, Jakub Jelinek wrote:
> On Mon, Nov 23, 2015 at 11:53:40AM -0500, David Malcolm wrote:
> > Does the following look like the kind of thing you had in mind?  (just
> > the tree.def part for now).   Presumably usable for both lvalues and
> > rvalues, where the thing it wraps is what's important.  It merely exists
> > to add an EXPR_LOCATION, for a usage of the wrapped thing.
> 
> Yes, but please see with Jason, Richard and perhaps others if they are ok
> with that too before spending too much time in that direction.
> All occurrences of it would have to be folded away during the gimplification
> at latest, this shouldn't be something we use in the middle-end.

I'd expect LOCATION_EXPR be defined in c-family/c-common.def, not tree.def.
And I'd think it shouldn't survive genericizing, thus never leak into the ME.

	Marek

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-23 17:09               ` Marek Polacek
@ 2015-11-23 19:45                 ` Jason Merrill
  2015-11-24  9:42                   ` Richard Biener
  0 siblings, 1 reply; 40+ messages in thread
From: Jason Merrill @ 2015-11-23 19:45 UTC (permalink / raw)
  To: Marek Polacek, Jakub Jelinek; +Cc: David Malcolm, Richard Biener, GCC Patches

On 11/23/2015 12:07 PM, Marek Polacek wrote:
> On Mon, Nov 23, 2015 at 05:57:54PM +0100, Jakub Jelinek wrote:
>> On Mon, Nov 23, 2015 at 11:53:40AM -0500, David Malcolm wrote:
>>> Does the following look like the kind of thing you had in mind?  (just
>>> the tree.def part for now).   Presumably usable for both lvalues and
>>> rvalues, where the thing it wraps is what's important.  It merely exists
>>> to add an EXPR_LOCATION, for a usage of the wrapped thing.
>>
>> Yes, but please see with Jason, Richard and perhaps others if they are ok
>> with that too before spending too much time in that direction.
>> All occurrences of it would have to be folded away during the gimplification
>> at latest, this shouldn't be something we use in the middle-end.
>
> I'd expect LOCATION_EXPR be defined in c-family/c-common.def, not tree.def.
> And I'd think it shouldn't survive genericizing, thus never leak into the ME.

Makes sense.

Jason


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-23 19:45                 ` Jason Merrill
@ 2015-11-24  9:42                   ` Richard Biener
  2015-11-24 11:08                     ` David Malcolm
  0 siblings, 1 reply; 40+ messages in thread
From: Richard Biener @ 2015-11-24  9:42 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Marek Polacek, Jakub Jelinek, David Malcolm, GCC Patches

On Mon, Nov 23, 2015 at 8:25 PM, Jason Merrill <jason@redhat.com> wrote:
> On 11/23/2015 12:07 PM, Marek Polacek wrote:
>>
>> On Mon, Nov 23, 2015 at 05:57:54PM +0100, Jakub Jelinek wrote:
>>>
>>> On Mon, Nov 23, 2015 at 11:53:40AM -0500, David Malcolm wrote:
>>>>
>>>> Does the following look like the kind of thing you had in mind?  (just
>>>> the tree.def part for now).   Presumably usable for both lvalues and
>>>> rvalues, where the thing it wraps is what's important.  It merely exists
>>>> to add an EXPR_LOCATION, for a usage of the wrapped thing.
>>>
>>>
>>> Yes, but please see with Jason, Richard and perhaps others if they are ok
>>> with that too before spending too much time in that direction.
>>> All occurrences of it would have to be folded away during the
>>> gimplification
>>> at latest, this shouldn't be something we use in the middle-end.
>>
>>
>> I'd expect LOCATION_EXPR be defined in c-family/c-common.def, not
>> tree.def.
>> And I'd think it shouldn't survive genericizing, thus never leak into the
>> ME.
>
>
> Makes sense.

OTOH then the FEs need to strip trees of it if they ever want to use
sth from fold-const.c for example.  NON_LVALUE_EXPR is not
in c-family/c-common.def either... (and conveniently stripped with
STRIP_NOPS and friends).

Ok, I _would_ like to move NON_LVALUE_EXPR to the C frontend
family as well...  so I guess that would be a nice starting project
and if you can make that work you can add LOCATION_EXPR to
the C family only.

Richard.

> Jason
>
>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-24  9:42                   ` Richard Biener
@ 2015-11-24 11:08                     ` David Malcolm
  2015-11-24 11:50                       ` Richard Biener
  2015-11-24 12:15                       ` Marek Polacek
  0 siblings, 2 replies; 40+ messages in thread
From: David Malcolm @ 2015-11-24 11:08 UTC (permalink / raw)
  To: Richard Biener; +Cc: Jason Merrill, Marek Polacek, Jakub Jelinek, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 5157 bytes --]

On Tue, 2015-11-24 at 10:40 +0100, Richard Biener wrote:
> On Mon, Nov 23, 2015 at 8:25 PM, Jason Merrill <jason@redhat.com> wrote:
> > On 11/23/2015 12:07 PM, Marek Polacek wrote:
> >>
> >> On Mon, Nov 23, 2015 at 05:57:54PM +0100, Jakub Jelinek wrote:
> >>>
> >>> On Mon, Nov 23, 2015 at 11:53:40AM -0500, David Malcolm wrote:
> >>>>
> >>>> Does the following look like the kind of thing you had in mind?  (just
> >>>> the tree.def part for now).   Presumably usable for both lvalues and
> >>>> rvalues, where the thing it wraps is what's important.  It merely exists
> >>>> to add an EXPR_LOCATION, for a usage of the wrapped thing.
> >>>
> >>>
> >>> Yes, but please see with Jason, Richard and perhaps others if they are ok
> >>> with that too before spending too much time in that direction.
> >>> All occurrences of it would have to be folded away during the
> >>> gimplification
> >>> at latest, this shouldn't be something we use in the middle-end.
> >>
> >>
> >> I'd expect LOCATION_EXPR be defined in c-family/c-common.def, not
> >> tree.def.
> >> And I'd think it shouldn't survive genericizing, thus never leak into the
> >> ME.
> >
> >
> > Makes sense.

FWIW, attached is a work-in-progress patch for the LOCATION_EXPR
approach.  This one
* adds LOCATION_EXPR to cp/cp-tree.def, rather than to c-common.def (my
thinking being that if it's only being used in the C++ FE, make it
specific to it)
* LOCATION_EXPR wrapper nodes are created for VAR_DECL, PARM_DECL (I'm
not sure if I want to create them for STRING_CST)
* strips them out in cp_genericize_r
* verifies their absence in cp_gimplify_expr via a gcc_unreachable
* lots of use of STRIP_LOCATION_EXPRS around places which expect a
certain kind of node.  This feels like a game of whack-a-mole.  I've got
increasing amounts of the C++ stdlib to parse, but am running into
various folding issues:

x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42: error:
‘(std::size_t)0’ is not a constant expression
       static const size_t _S_alignment = 0;
                                          ^

which doesn't seem to be easily fixable; I'd already put a cp_fully_fold
into cp/constexpr.c:verify_constant, but this is:

(gdb) call debug_tree (t)
 <nop_expr 0x7fffef03d580
    type <integer_type 0x7ffff1632f18 size_t unsigned type_6 DI
        size <integer_cst 0x7ffff1891e58 constant 64>
        unit size <integer_cst 0x7ffff1891e70 constant 8>
        align 64 symtab 0 alias set -1 canonical type 0x7ffff18959d8
precision 64 min <integer_cst 0x7ffff18b3138 0> max <integer_cst
0x7ffff18a35e0 18446744073709551615>>
    constant
    arg 0 <location_expr 0x7fffef03d4e0
        type <integer_type 0x7ffff18957e0 int asm_written public type_6
SI
            size <integer_cst 0x7ffff18b30a8 constant 32>
            unit size <integer_cst 0x7ffff18b30c0 constant 4>
            align 32 symtab -243642208 alias set -1 canonical type
0x7ffff18957e0 precision 32 min <integer_cst 0x7ffff18b3060 -2147483648>
max <integer_cst 0x7ffff18b3078 2147483647>
            pointer_to_this <pointer_type 0x7ffff18b7930>>
        constant
        arg 0 <integer_cst 0x7ffff18b31f8 constant 0>
        /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 start: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 finish: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42>
    /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 start: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 finish: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42>

which isn't folded here by cp_fully_fold since we're in
processing_template_decl; STRIP_NOPS etc can't do it since LOCATION_EXPR
is frontend-specific.

Any thoughts on how to work around this?


> OTOH then the FEs need to strip trees of it if they ever want to use
> sth from fold-const.c for example.  NON_LVALUE_EXPR is not
> in c-family/c-common.def either... (and conveniently stripped with
> STRIP_NOPS and friends).

Just to clarify, do you mean "NON_LVALUE_EXPR is *not* conveniently
stripped by STRIP_NOPS and friends"?  As far as I can tell, STRIP_NOPS
etc are all frontend-independent, hence frontend-specific tree types
can't be handled there.


> Ok, I _would_ like to move NON_LVALUE_EXPR to the C frontend
> family as well...  so I guess that would be a nice starting project
> and if you can make that work you can add LOCATION_EXPR to
> the C family only.

At a higher level, I'm nervous about feature-creep here, relative to the
v2 patch; that one (mostly) worked, without creating a new tree type, in
that it captured the locations (and thus ranges) for the leaf nodes of
the parse tree for just long enough to let them be used when building
more interesting expressions, which is where the range information is
useful.

Dave

[-- Attachment #2: wip.patch --]
[-- Type: text/x-patch, Size: 63442 bytes --]

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 8cdda62..623c2ef 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -1087,6 +1087,9 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
   bool fromref = false;
   tree qualified_to;
 
+  if (expr)
+    STRIP_LOCATION_EXPRS (expr);
+
   to = non_reference (to);
   if (TREE_CODE (from) == REFERENCE_TYPE)
     {
@@ -5768,7 +5771,11 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
     case REALPART_EXPR:
     case IMAGPART_EXPR:
     case ABS_EXPR:
-      return cp_build_unary_op (code, arg1, candidates != 0, complain);
+      {
+	tree result = cp_build_unary_op (code, arg1, candidates != 0, complain);
+	protected_set_expr_location (result, loc);
+	return result;
+      }
 
     case ARRAY_REF:
       return cp_build_array_ref (input_location, arg1, arg2, complain);
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 459173d..1115403 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1460,6 +1460,9 @@ reduced_constant_expression_p (tree t)
 	  return false;
       return true;
 
+    case LOCATION_EXPR:
+      return reduced_constant_expression_p (TREE_OPERAND (t, 0));
+
     default:
       /* FIXME are we calling this too much?  */
       return initializer_constant_valid_p (t, TREE_TYPE (t)) != NULL_TREE;
@@ -1479,6 +1482,8 @@ static bool
 verify_constant (tree t, bool allow_non_constant, bool *non_constant_p,
 		 bool *overflow_p)
 {
+  t = cp_fully_fold (t);
+
   if (!*non_constant_p && !reduced_constant_expression_p (t))
     {
       if (!allow_non_constant)
@@ -3563,6 +3568,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case VIEW_CONVERT_EXPR:
     case NOP_EXPR:
     case UNARY_PLUS_EXPR:
+    case LOCATION_EXPR:
       {
 	enum tree_code tcode = TREE_CODE (t);
 	tree oldop = TREE_OPERAND (t, 0);
@@ -3589,7 +3595,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	if (op == oldop && tcode != UNARY_PLUS_EXPR)
 	  /* We didn't fold at the top so we could check for ptr-int
 	     conversion.  */
-	  return fold (t);
+	  return cp_fully_fold (t);
 	if (tcode == UNARY_PLUS_EXPR)
 	  r = fold_convert (TREE_TYPE (t), op);
 	else
@@ -4753,6 +4759,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case EMPTY_CLASS_EXPR:
       return false;
 
+    case LOCATION_EXPR:
+      return RECUR (TREE_OPERAND (t, 0), want_rval);
+
     default:
       if (objc_is_property_ref (t))
 	return false;
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 99d0cfb..ca989d4 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -702,6 +702,9 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
     case BREAK_STMT:
       gcc_unreachable ();
 
+    case LOCATION_EXPR:
+      gcc_unreachable ();
+
     case OMP_FOR:
     case OMP_SIMD:
     case OMP_DISTRIBUTE:
@@ -1356,6 +1359,13 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 	   || TREE_CODE (stmt) == OMP_DISTRIBUTE
 	   || TREE_CODE (stmt) == OMP_TASKLOOP)
     genericize_omp_for_stmt (stmt_p, walk_subtrees, data);
+  else if (TREE_CODE (stmt) == LOCATION_EXPR)
+    {
+      /* Strip away LOCATION_EXPR nodes.  */
+      *stmt_p = TREE_OPERAND (stmt, 0);
+      /* *stmt_p has changed, tail recurse to handle it again.  */
+      return cp_genericize_r (stmt_p, walk_subtrees, data);
+    }
   else if ((flag_sanitize
 	    & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR))
 	   && !wtd->no_sanitize_p)
@@ -2213,6 +2223,10 @@ cp_fold (tree x)
       x = fold (x);
       break;
 
+    case LOCATION_EXPR:
+      x = cp_fold (TREE_OPERAND (x, 0));
+      break;
+
     default:
       return org_x;
     }
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index 2758252..816b236 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -306,6 +306,7 @@ cp_common_init_ts (void)
   MARK_TS_TYPED (UNARY_RIGHT_FOLD_EXPR);
   MARK_TS_TYPED (BINARY_LEFT_FOLD_EXPR);
   MARK_TS_TYPED (BINARY_RIGHT_FOLD_EXPR);
+  MARK_TS_TYPED (LOCATION_EXPR);
 }
 
 #include "gt-cp-cp-objcp-common.h"
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 7df72c5..20e6b11 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -582,6 +582,12 @@ DEFTREECODE (PARM_CONSTR, "parm_constr", tcc_expression, 2)
 DEFTREECODE (CONJ_CONSTR, "conj_constr", tcc_expression, 2)
 DEFTREECODE (DISJ_CONSTR, "disj_constr", tcc_expression, 2)
 
+/* Wrapper used by the C++ frontend during parsing to add a source code
+   location to an expression, either one that doesn't have one (such as
+   an INTEGER_CST), or to a usage of a variable (e.g. PARAM_DECL or
+   VAR_DECL), where we want to record the site in the source where the
+   variable was *used* rather than where it was declared.  */
+DEFTREECODE (LOCATION_EXPR, "location_expr", tcc_unary, 1)
 
 /*
 Local variables:
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 160bf1e..4482112 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -256,6 +256,13 @@ c-common.h, not after.
 #define THUNK_FUNCTION_CHECK(NODE) (NODE)
 #endif
 \f
+/* Language-dependent macro for stripping away location wrapper nodes.  */
+
+#define STRIP_LOCATION_EXPRS(EXP) \
+  while (TREE_CODE (EXP) == LOCATION_EXPR) \
+    (EXP) = TREE_OPERAND ((EXP), 0)
+
+\f
 /* Language-dependent contents of an identifier.  */
 
 struct GTY(()) lang_identifier {
@@ -271,6 +278,7 @@ struct GTY(()) lang_identifier {
 inline lang_identifier*
 identifier_p (tree t)
 {
+  STRIP_LOCATION_EXPRS (t);
   if (TREE_CODE (t) == IDENTIFIER_NODE)
     return (lang_identifier*) t;
   return NULL;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 38548c7..8ac6ca1 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -2750,6 +2750,10 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
       pp_string (pp, M_("*this"));
       break;
 
+    case LOCATION_EXPR:
+      dump_expr (pp, TREE_OPERAND (t, 0), flags);
+      break;
+
       /*  This list is incomplete, but should suffice for now.
 	  It is very important that `sorry' does not call
 	  `report_error_function'.  That could cause an infinite loop.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 0e1116b..aea0a75 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -1414,6 +1414,8 @@ make_id_declarator (tree qualifying_scope, tree unqualified_name,
   if (qualifying_scope && TYPE_P (qualifying_scope))
     qualifying_scope = TYPE_MAIN_VARIANT (qualifying_scope);
 
+  STRIP_LOCATION_EXPRS (unqualified_name);
+
   gcc_assert (identifier_p (unqualified_name)
 	      || TREE_CODE (unqualified_name) == BIT_NOT_EXPR
 	      || TREE_CODE (unqualified_name) == TEMPLATE_ID_EXPR);
@@ -1976,7 +1978,7 @@ static tree cp_parser_postfix_open_square_expression
 static tree cp_parser_postfix_dot_deref_expression
   (cp_parser *, enum cpp_ttype, tree, bool, cp_id_kind *, location_t);
 static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
-  (cp_parser *, int, bool, bool, bool *);
+  (cp_parser *, int, bool, bool, bool *, location_t * = NULL);
 /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
 enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
 static void cp_parser_pseudo_destructor_name
@@ -3665,6 +3667,18 @@ cp_parser_pop_lexer (cp_parser *parser)
   cp_lexer_set_source_position_from_token (parser->lexer->next_token);
 }
 
+/* FIXME.  */
+static tree
+wrap_with_location (tree value, location_t loc)
+{
+#if 1
+  tree wrapper = build1_loc (loc, LOCATION_EXPR, TREE_TYPE (value), value);
+  return wrapper;
+#else
+  return value;
+#endif
+}
+
 /* Lexical conventions [gram.lex]  */
 
 /* Parse an identifier.  Returns an IDENTIFIER_NODE representing the
@@ -3678,7 +3692,10 @@ cp_parser_identifier (cp_parser* parser)
   /* Look for the identifier.  */
   token = cp_parser_require (parser, CPP_NAME, RT_NAME);
   /* Return the value.  */
-  return token ? token->u.value : error_mark_node;
+  if (token)
+    return token->u.value; // FIXME //use_as_rvalue (token->u.value, token->location); // FIXME: what if an lvalue?
+  else
+    return error_mark_node;
 }
 
 /* Parse a sequence of adjacent string constants.  Returns a
@@ -3717,6 +3734,8 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
       return error_mark_node;
     }
 
+  location_t loc = tok->location;
+
   if (cpp_userdef_string_p (tok->type))
     {
       string_tree = USERDEF_LITERAL_VALUE (tok->u.value);
@@ -3754,11 +3773,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
     }
   else
     {
+      location_t last_tok_loc;
       gcc_obstack_init (&str_ob);
       count = 0;
 
       do
 	{
+	  last_tok_loc = tok->location;
 	  cp_lexer_consume_token (parser->lexer);
 	  count++;
 	  str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
@@ -3813,6 +3834,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 	}
       while (cp_parser_is_string_literal (tok));
 
+      /* A string literal built by concatenation has its caret=start at
+	 the start of the initial string, and its finish at the finish of
+	 the final string literal.  */
+      loc = make_location (loc, loc,
+			   get_range_from_loc (line_table,
+					       last_tok_loc).m_finish);
+
       strs = (cpp_string *) obstack_finish (&str_ob);
     }
 
@@ -3865,7 +3893,11 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
   if (count > 1)
     obstack_free (&str_ob, 0);
 
+#if 0
   return value;
+#else
+  return wrap_with_location (value, loc);
+#endif
 }
 
 /* Look up a literal operator with the name and the exact arguments.  */
@@ -4614,7 +4646,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	  if (!cast_p)
 	    cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
 	}
-      return token->u.value;
+      return wrap_with_location (token->u.value, token->location);
 
     case CPP_CHAR_USERDEF:
     case CPP_CHAR16_USERDEF:
@@ -4675,6 +4707,8 @@ cp_parser_primary_expression (cp_parser *parser,
 	tree expr;
 	bool saved_greater_than_is_operator_p;
 
+	location_t open_paren_loc = token->location;
+
 	/* Consume the `('.  */
 	cp_lexer_consume_token (parser->lexer);
 	/* Within a parenthesized expression, a `>' token is always
@@ -4719,7 +4753,11 @@ cp_parser_primary_expression (cp_parser *parser,
 	   template-parameter-list now.  */
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
+
 	/* Consume the `)'.  */
+	token = cp_lexer_peek_token (parser->lexer);
+	location_t close_paren_loc = token->location;
+        set_source_range (expr, open_paren_loc, close_paren_loc);
 	if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
 	    && !cp_parser_uncommitted_to_tentative_parse_p (parser))
 	  cp_parser_skip_to_end_of_statement (parser);
@@ -4760,20 +4798,20 @@ cp_parser_primary_expression (cp_parser *parser,
 	  /* These two are the boolean literals.  */
 	case RID_TRUE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_true_node;
+	  return wrap_with_location (boolean_true_node, token->location);
 	case RID_FALSE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_false_node;
+	  return wrap_with_location (boolean_false_node, token->location);
 
 	  /* The `__null' literal.  */
 	case RID_NULL:
 	  cp_lexer_consume_token (parser->lexer);
-	  return null_node;
+	  return wrap_with_location (null_node, token->location);
 
 	  /* The `nullptr' literal.  */
 	case RID_NULLPTR:
 	  cp_lexer_consume_token (parser->lexer);
-	  return nullptr_node;
+	  return wrap_with_location (nullptr_node, token->location);
 
 	  /* Recognize the `this' keyword.  */
 	case RID_THIS:
@@ -4921,6 +4959,7 @@ cp_parser_primary_expression (cp_parser *parser,
     case CPP_TEMPLATE_ID:
     case CPP_NESTED_NAME_SPECIFIER:
       {
+      id_expression:
 	tree id_expression;
 	tree decl;
 	const char *error_msg;
@@ -4928,7 +4967,6 @@ cp_parser_primary_expression (cp_parser *parser,
 	bool done;
 	cp_token *id_expr_token;
 
-      id_expression:
 	/* Parse the id-expression.  */
 	id_expression
 	  = cp_parser_id_expression (parser,
@@ -6116,6 +6154,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
   loc = token->location;
+  location_t start_loc = get_range_from_loc (line_table, loc).m_start;
+
   /* Some of the productions are determined by keywords.  */
   keyword = token->keyword;
   switch (keyword)
@@ -6159,7 +6199,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* And the expression which is being cast.  */
 	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
 	expression = cp_parser_expression (parser, & idk, /*cast_p=*/true);
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN,
+						   RT_CLOSE_PAREN);
+	location_t end_loc = close_paren ?
+	  close_paren->location : UNKNOWN_LOCATION;
 
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
@@ -6192,6 +6235,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  default:
 	    gcc_unreachable ();
 	  }
+
+	/* Construct a location e.g. :
+	     reinterpret_cast <int *> (expr)
+	     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	   ranging from the start of the "*_cast" token to the final closing
+	   paren, with the caret at the start.  */
+	location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc);
+	protected_set_expr_location (postfix_expression, cp_cast_loc);
       }
       break;
 
@@ -6471,6 +6522,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 							postfix_expression,
 							false,
 							decltype_p);
+          set_source_range (postfix_expression, start_loc,
+                            EXPR_LOCATION (postfix_expression));
 	  idk = CP_ID_KIND_NONE;
           is_member_access = false;
 	  break;
@@ -6484,6 +6537,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    bool saved_non_integral_constant_expression_p = false;
 	    tsubst_flags_t complain = complain_flags (decltype_p);
 	    vec<tree, va_gc> *args;
+	    location_t close_paren_loc;
 
             is_member_access = false;
 
@@ -6502,7 +6556,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    args = (cp_parser_parenthesized_expression_list
 		    (parser, non_attr,
 		     /*cast_p=*/false, /*allow_expansion_p=*/true,
-		     /*non_constant_p=*/NULL));
+		     /*non_constant_p=*/NULL,
+		     /*close_paren_loc=*/&close_paren_loc));
 	    if (is_builtin_constant_p)
 	      {
 		parser->integral_constant_expression_p
@@ -6644,7 +6699,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 				    koenig_p,
 				    complain);
 
-	    protected_set_expr_location (postfix_expression, token->location);
+	    location_t combined_loc = make_location (token->location,
+						     start_loc,
+						     close_paren_loc);
+	    protected_set_expr_location (postfix_expression, combined_loc);
 
 	    /* The POSTFIX_EXPRESSION is certainly no longer an id.  */
 	    idk = CP_ID_KIND_NONE;
@@ -6705,7 +6763,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  if (pidk_return != NULL)
 	    * pidk_return = idk;
           if (member_access_only_p)
-            return is_member_access? postfix_expression : error_mark_node;
+            return is_member_access
+              ? postfix_expression
+              : error_mark_node;
           else
             return postfix_expression;
 	}
@@ -7107,7 +7167,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 int is_attribute_list,
 					 bool cast_p,
                                          bool allow_expansion_p,
-					 bool *non_constant_p)
+					 bool *non_constant_p,
+					 location_t *close_paren_loc)
 {
   vec<tree, va_gc> *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
@@ -7211,6 +7272,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 	cp_lexer_consume_token (parser->lexer);
       }
 
+  if (close_paren_loc)
+    *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
     {
       int ending;
@@ -7611,6 +7675,14 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 				     /*cast_p=*/false,
 				     /*decltype*/false,
 				     pidk);
+
+      /* Make a location:
+	    OP_TOKEN  CAST_EXPRESSION
+	    ^~~~~~~~~~~~~~~~~~~~~~~~~
+	 with start==caret at the operator token, and
+	 extending to the end of the cast_expression.  */
+      loc = make_location (loc, loc, get_expr_finish (cast_expression));
+
       /* Now, build an appropriate representation.  */
       switch (unary_operator)
 	{
@@ -7663,6 +7735,8 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	  gcc_unreachable ();
 	}
 
+      protected_set_expr_location (expression, loc);
+
       if (non_constant_p != NIC_NONE
 	  && cp_parser_non_integral_constant_expression (parser,
 							 non_constant_p))
@@ -7726,6 +7800,8 @@ cp_parser_new_expression (cp_parser* parser)
   tree nelts = NULL_TREE;
   tree ret;
 
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   /* Look for the optional `::' operator.  */
   global_scope_p
     = (cp_parser_global_scope_opt (parser,
@@ -7820,6 +7896,17 @@ cp_parser_new_expression (cp_parser* parser)
   if (initializer != NULL)
     release_tree_vector (initializer);
 
+  /* Construct a location e.g.:
+        ptr = new int[100]
+              ^~~~~~~~~~~~
+     with caret == start at the start of the "new" token, and the end
+     at the end of the final token we consumed.  */
+  cp_token *end_tok = cp_lexer_previous_token (parser->lexer);
+  location_t end_loc = get_range_from_loc (line_table,
+                                           end_tok->location).m_finish;
+  location_t combined_loc = make_location (start_loc, start_loc, end_loc);
+  protected_set_expr_location (ret, combined_loc);
+
   return ret;
 }
 
@@ -8213,7 +8300,9 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
       parser->type_definition_forbidden_message
 	= G_("types may not be defined in casts");
       /* Consume the `('.  */
-      cp_lexer_consume_token (parser->lexer);
+      cp_token *open_paren = cp_lexer_consume_token (parser->lexer);
+      location_t open_paren_loc = open_paren->location;
+
       /* A very tricky bit is that `(struct S) { 3 }' is a
 	 compound-literal (which we permit in C++ as an extension).
 	 But, that construct is not a cast-expression -- it is a
@@ -8315,7 +8404,15 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 		return error_mark_node;
 
 	      /* Perform the cast.  */
-	      expr = build_c_cast (input_location, type, expr);
+	      /* Make a location:
+		   (TYPE) EXPR
+		   ^~~~~~~~~~~
+		 with start==caret at the open paren, extending to the
+		 end of "expr".  */
+	      location_t cast_loc = make_location (open_paren_loc,
+						   open_paren_loc,
+						   get_expr_finish (expr));
+	      expr = build_c_cast (cast_loc, type, expr);
 	      return expr;
 	    }
 	}
@@ -8558,6 +8655,11 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 				      maybe_constant_value (rhs));
 
       overload = NULL;
+
+      location_t combined_loc = make_location (current.loc,
+					       get_expr_start (current.lhs),
+					       get_expr_finish (rhs));
+
       /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type ==
 	 ERROR_MARK for everything that is not a binary expression.
 	 This makes warn_about_parentheses miss some warnings that
@@ -8568,18 +8670,18 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
       if (no_toplevel_fold_p
 	  && lookahead_prec <= current.prec
 	  && sp == stack)
-	current.lhs = build2 (current.tree_type,
-			      TREE_CODE_CLASS (current.tree_type)
-			      == tcc_comparison
-			      ? boolean_type_node : TREE_TYPE (current.lhs),
-			      current.lhs, rhs);
+	current.lhs = build2_loc (combined_loc,
+				  current.tree_type,
+				  TREE_CODE_CLASS (current.tree_type)
+				  == tcc_comparison
+				  ? boolean_type_node : TREE_TYPE (current.lhs),
+				  current.lhs, rhs);
       else
-	current.lhs = build_x_binary_op (current.loc, current.tree_type,
+	current.lhs = build_x_binary_op (combined_loc, current.tree_type,
 					 current.lhs, current.lhs_type,
 					 rhs, rhs_type, &overload,
 					 complain_flags (decltype_p));
       current.lhs_type = current.tree_type;
-      protected_set_expr_location (current.lhs, current.loc);
 
       /* If the binary operator required the use of an overloaded operator,
 	 then this expression cannot be an integral constant-expression.
@@ -8662,6 +8764,15 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
   c_inhibit_evaluation_warnings -=
     folded_logical_or_expr == truthvalue_true_node;
 
+  /* Make a location:
+       LOGICAL_OR_EXPR ? EXPR : ASSIGNMENT_EXPR
+       ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
+     with the caret at the "?", ranging from the start of
+     the logical_or_expr to the end of the assignment_expr.  */
+  loc = make_location (loc,
+		       get_expr_start (logical_or_expr),
+		       get_expr_finish (assignment_expr));
+
   /* Build the conditional-expression.  */
   return build_x_conditional_expr (loc, logical_or_expr,
 				   expr,
@@ -8717,7 +8828,8 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 	      location_t saved_input_location;
 
 	      /* Parse the right-hand side of the assignment.  */
-	      tree rhs = cp_parser_initializer_clause (parser, &non_constant_p);
+	      tree rhs = cp_parser_initializer_clause (parser,
+							  &non_constant_p);
 
 	      if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
 		maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
@@ -8728,13 +8840,22 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 							      NIC_ASSIGNMENT))
 		return error_mark_node;
 	      /* Build the assignment expression.  Its default
-		 location is the location of the '=' token.  */
+		 location:
+		   LHS = RHS
+		   ~~~~^~~~~
+		 is the location of the '=' token as the
+		 caret, ranging from the start of the lhs to the
+		 end of the rhs.  */
 	      saved_input_location = input_location;
+	      loc = make_location (loc,
+				   get_expr_start (expr),
+				   get_expr_finish (rhs));
 	      input_location = loc;
 	      expr = build_x_modify_expr (loc, expr,
 					  assignment_operator,
 					  rhs,
 					  complain_flags (decltype_p));
+	      protected_set_expr_location (expr, loc);
 	      input_location = saved_input_location;
 	    }
 	}
@@ -8875,9 +8996,17 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
       if (!expression)
 	expression = assignment_expression;
       else
-	expression = build_x_compound_expr (loc, expression,
-					    assignment_expression,
-					    complain_flags (decltype_p));
+	{
+	  /* Create a location with caret at the comma, ranging
+	     from the start of the LHS to the end of the RHS.  */
+	  loc = make_location (loc,
+			       get_expr_start (expression),
+			       get_expr_finish (assignment_expression));
+	  expression = build_x_compound_expr (loc, expression,
+					      assignment_expression,
+					      complain_flags (decltype_p));
+	  //expression.set_location (loc);
+	}
       /* If the next token is not a comma, or we're in a fold-expression, then
 	 we are done with the expression.  */
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)
@@ -12501,6 +12630,7 @@ cp_parser_linkage_specification (cp_parser* parser)
 
   /* Look for the string-literal.  */
   linkage = cp_parser_string_literal (parser, false, false);
+  STRIP_LOCATION_EXPRS (linkage);
 
   /* Transform the literal into an identifier.  If the literal is a
      wide-character string, or contains embedded NULs, then we can't
@@ -13333,6 +13463,9 @@ cp_parser_operator (cp_parser* parser)
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
+
+  location_t start_loc = token->location;
+
   /* Figure out which operator we have.  */
   switch (token->type)
     {
@@ -13349,7 +13482,7 @@ cp_parser_operator (cp_parser* parser)
 	  break;
 
 	/* Consume the `new' or `delete' token.  */
-	cp_lexer_consume_token (parser->lexer);
+	location_t end_loc = cp_lexer_consume_token (parser->lexer)->location;
 
 	/* Peek at the next token.  */
 	token = cp_lexer_peek_token (parser->lexer);
@@ -13360,7 +13493,8 @@ cp_parser_operator (cp_parser* parser)
 	    /* Consume the `[' token.  */
 	    cp_lexer_consume_token (parser->lexer);
 	    /* Look for the `]' token.  */
-	    cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
+	    end_loc = cp_parser_require (parser, CPP_CLOSE_SQUARE,
+                                         RT_CLOSE_SQUARE)->location;
 	    id = ansi_opname (op == NEW_EXPR
 			      ? VEC_NEW_EXPR : VEC_DELETE_EXPR);
 	  }
@@ -13368,7 +13502,9 @@ cp_parser_operator (cp_parser* parser)
 	else
 	  id = ansi_opname (op);
 
-	return id;
+	location_t loc = make_location (start_loc, start_loc, end_loc);
+
+	return id; // FIXME: what to do about loc
       }
 
     case CPP_PLUS:
@@ -13614,7 +13750,7 @@ cp_parser_operator (cp_parser* parser)
       id = error_mark_node;
     }
 
-  return id;
+  return id; // FIXME: cp_expr (id, start_loc);
 }
 
 /* Parse a template-declaration.
@@ -17357,6 +17493,7 @@ cp_parser_using_declaration (cp_parser* parser,
 	  decl = cp_parser_lookup_name_simple (parser,
 					       identifier,
 					       token->location);
+	  STRIP_LOCATION_EXPRS (decl);
 	  if (decl == error_mark_node)
 	    cp_parser_name_lookup_error (parser, identifier,
 					 decl, NLE_NULL,
@@ -17623,6 +17760,8 @@ cp_parser_asm_definition (cp_parser* parser)
     return;
   /* Look for the string.  */
   string = cp_parser_string_literal (parser, false, false);
+  STRIP_LOCATION_EXPRS (string);
+
   if (string == error_mark_node)
     {
       cp_parser_skip_to_closing_parenthesis (parser, true, false,
@@ -20696,6 +20835,7 @@ cp_parser_class_name (cp_parser *parser,
     }
 
   decl = cp_parser_maybe_treat_template_as_class (decl, class_head_p);
+  STRIP_LOCATION_EXPRS (decl);
 
   /* If this is a typename, create a TYPENAME_TYPE.  */
   if (typename_p && decl != error_mark_node)
@@ -22876,6 +23016,7 @@ cp_parser_asm_specification_opt (cp_parser* parser)
 
   /* Look for the string-literal.  */
   asm_specification = cp_parser_string_literal (parser, false, false);
+  STRIP_LOCATION_EXPRS (asm_specification);
 
   /* Look for the `)'.  */
   cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
@@ -22928,6 +23069,7 @@ cp_parser_asm_operand_list (cp_parser* parser)
 	name = NULL_TREE;
       /* Look for the string-literal.  */
       string_literal = cp_parser_string_literal (parser, false, false);
+      STRIP_LOCATION_EXPRS (string_literal);
 
       /* Look for the `('.  */
       cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
@@ -22976,6 +23118,7 @@ cp_parser_asm_clobber_list (cp_parser* parser)
 
       /* Look for the string literal.  */
       string_literal = cp_parser_string_literal (parser, false, false);
+      STRIP_LOCATION_EXPRS (string_literal);
       /* Add it to the list.  */
       clobbers = tree_cons (NULL_TREE, string_literal, clobbers);
       /* If the next token is not a `,', then the list is
@@ -24317,7 +24460,11 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
 
   maybe_record_typedef_use (decl);
 
-  return decl;
+  if (TREE_CODE (decl) == VAR_DECL
+      || TREE_CODE (decl) == PARM_DECL)
+    return wrap_with_location (decl, name_location);
+  else
+    return decl;
 }
 
 /* Like cp_parser_lookup_name, but for use in the typical case where
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index e7e5d8e..df95da9 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2463,7 +2463,12 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
 tree
 finish_increment_expr (tree expr, enum tree_code code)
 {
-  return build_x_unary_op (input_location, code, expr, tf_warning_or_error);
+  /* input_location holds the location of the trailing operator token.  */
+  tree result = build_x_unary_op (input_location, code, expr,
+				     tf_warning_or_error);
+  set_source_range (result, get_expr_start (expr),
+		    get_finish (input_location));
+  return result;
 }
 
 /* Finish a use of `this'.  Returns an expression for `this'.  */
@@ -2558,10 +2563,12 @@ finish_pseudo_destructor_expr (tree object, tree scope, tree destructor,
 /* Finish an expression of the form CODE EXPR.  */
 
 tree
-finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
+finish_unary_op_expr (location_t op_loc, enum tree_code code, tree expr,
 		      tsubst_flags_t complain)
 {
-  tree result = build_x_unary_op (loc, code, expr, complain);
+  location_t combined_loc = make_location (op_loc,
+					   op_loc, get_expr_finish (expr));
+  tree result = build_x_unary_op (combined_loc, code, expr, complain);
   tree result_ovl, expr_ovl;
 
   if (!(complain & tf_warning))
@@ -2581,9 +2588,9 @@ finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
     result_ovl = cp_fully_fold (result_ovl);
 
   if (CONSTANT_CLASS_P (result_ovl) && TREE_OVERFLOW_P (result_ovl))
-    overflow_warning (input_location, result_ovl);
+    overflow_warning (combined_loc, result_ovl);
 
-  return result;
+  return result; // cp_expr (result, combined_loc);
 }
 
 /* Finish a compound-literal expression.  TYPE is the type to which
@@ -3669,7 +3676,7 @@ finish_id_expression (tree id_expression,
 	}
     }
 
-  return decl;
+  return decl; // return use_as_rvalue (decl); // FIXME: is this neeeded?
 }
 
 /* Implement the __typeof keyword: Return the type of EXPR, suitable for
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index d2db31a..c68bba0 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -56,6 +56,8 @@ lvalue_kind (const_tree ref)
   cp_lvalue_kind op1_lvalue_kind = clk_none;
   cp_lvalue_kind op2_lvalue_kind = clk_none;
 
+  STRIP_LOCATION_EXPRS (ref);
+
   /* Expressions of reference type are sometimes wrapped in
      INDIRECT_REFs.  INDIRECT_REFs are just internal compiler
      representation, not part of the language, so we have to look
@@ -3598,6 +3600,7 @@ check_abi_tag_args (tree args, tree name)
   for (tree arg = args; arg; arg = TREE_CHAIN (arg))
     {
       tree elt = TREE_VALUE (arg);
+      STRIP_LOCATION_EXPRS (elt);
       if (TREE_CODE (elt) != STRING_CST
 	  || (!same_type_ignoring_top_level_qualifiers_p
 	      (strip_array_types (TREE_TYPE (elt)),
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 5f7d4bb..27df803 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-objc.h"
 #include "c-family/c-ubsan.h"
 #include "params.h"
+//#include "print-tree.h"
 
 static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
 static tree cp_build_function_call (tree, tree, tsubst_flags_t);
@@ -2435,7 +2436,11 @@ build_class_member_access_expr (tree object, tree member,
 	  member_type = cp_build_qualified_type (member_type, type_quals);
 	}
 
-      result = build3_loc (input_location, COMPONENT_REF, member_type,
+      location_t combined_loc =
+	make_location (input_location,
+		       get_expr_start (object),
+		       get_finish (input_location));
+      result = build3_loc (combined_loc, COMPONENT_REF, member_type,
 			   object, member, NULL_TREE);
 
       /* Mark the expression const or volatile, as appropriate.  Even
@@ -3669,6 +3674,12 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
       tree type = typetail ? TREE_VALUE (typetail) : 0;
       tree val = (**values)[i];
 
+#if 0
+      fprintf (stderr, "arg %i at start of loop body\n", i);
+      debug_tree (val);
+      inform (EXPR_LOCATION (val), "arg %i at start of loop body", i);
+#endif
+
       if (val == error_mark_node || type == error_mark_node)
 	return -1;
 
@@ -3683,12 +3694,14 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
             return -1;
 	}
 
+#if 0
       /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
 	 Strip such NOP_EXPRs, since VAL is used in non-lvalue context.  */
       if (TREE_CODE (val) == NOP_EXPR
 	  && TREE_TYPE (val) == TREE_TYPE (TREE_OPERAND (val, 0))
 	  && (type == 0 || TREE_CODE (type) != REFERENCE_TYPE))
 	val = TREE_OPERAND (val, 0);
+#endif
 
       if (type == 0 || TREE_CODE (type) != REFERENCE_TYPE)
 	{
@@ -3746,6 +3759,12 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
 
       if (typetail)
 	typetail = TREE_CHAIN (typetail);
+
+#if 0
+      fprintf (stderr, "values[%i] at end of loop body\n", i);
+      debug_tree ((**values)[i]);
+      inform (EXPR_LOCATION ((**values)[i]), "values[%i] at end of loop body", i);
+#endif
     }
 
   if (typetail != 0 && typetail != void_list_node)
@@ -4024,7 +4043,9 @@ cp_build_binary_op (location_t location,
     }
 
   /* Strip NON_LVALUE_EXPRs, etc., since we aren't using as an lvalue.  */
+  STRIP_LOCATION_EXPRS (op0);
   STRIP_TYPE_NOPS (op0);
+  STRIP_LOCATION_EXPRS (op1);
   STRIP_TYPE_NOPS (op1);
 
   /* DTRT if one side is an overloaded function, but complain about it.  */
@@ -5102,7 +5123,7 @@ cp_build_binary_op (location_t location,
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
-  result = build2 (resultcode, build_type, op0, op1);
+  result = build2_loc (location, resultcode, build_type, op0, op1);
   if (final_type != 0)
     result = cp_convert (final_type, result, complain);
 
@@ -5429,7 +5450,7 @@ build_nop (tree type, tree expr)
 {
   if (type == error_mark_node || error_operand_p (expr))
     return expr;
-  return build1 (NOP_EXPR, type, expr);
+  return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr);
 }
 
 /* Take the address of ARG, whatever that means under C++ semantics.
@@ -7254,9 +7275,11 @@ build_const_cast (tree type, tree expr, tsubst_flags_t complain)
 /* Like cp_build_c_cast, but for the c-common bits.  */
 
 tree
-build_c_cast (location_t /*loc*/, tree type, tree expr)
+build_c_cast (location_t loc, tree type, tree expr)
 {
-  return cp_build_c_cast (type, expr, tf_warning_or_error);
+  tree result = cp_build_c_cast (type, expr, tf_warning_or_error);
+  protected_set_expr_location (result, loc);
+  return result;
 }
 
 /* Build an expression representing an explicit C-style cast to type
@@ -7784,7 +7807,10 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 	  return rval;
 	}
     }
-  return cp_build_modify_expr (lhs, modifycode, rhs, complain);
+
+  tree result = cp_build_modify_expr (lhs, modifycode, rhs, complain);
+  protected_set_expr_location (result, loc);
+  return result;
 }
 
 /* Helper function for get_delta_difference which assumes FROM is a base
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 839091c..6d58bda 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1740,7 +1740,9 @@ build_x_arrow (location_t loc, tree expr, tsubst_flags_t complain)
 	  return expr;
 	}
 
-      return cp_build_indirect_ref (last_rval, RO_NULL, complain);
+      tree result = cp_build_indirect_ref (last_rval, RO_NULL, complain);
+      protected_set_expr_location (result, loc);
+      return result;
     }
 
   if (complain & tf_error)
diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 9e51b95..072f3a5 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -779,7 +779,7 @@ diagnostic_show_locus (diagnostic_context * context,
 {
   if (!context->show_caret
       || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
-      || diagnostic_location (diagnostic, 0) == context->last_location)
+      /*|| diagnostic_location (diagnostic, 0) == context->last_location*/)
     return;
 
   context->last_location = diagnostic_location (diagnostic, 0);
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
new file mode 100644
index 0000000..e2e801e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.c
@@ -0,0 +1,568 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret" } */
+
+/* This is a collection of unittests to verify that we're correctly
+   capturing the source code ranges of various kinds of expression.
+
+   It uses the various "diagnostic_test_*_expression_range_plugin"
+   plugins which handles "__emit_expression_range" by generating a warning
+   at the given source range of the input argument.  Each of the
+   different plugins do this at a different phase of the internal
+   representation (tree, gimple, etc), so we can verify that the
+   source code range information is valid at each phase.
+
+   We want to accept an expression of any type.  To do this in C, we
+   use variadic arguments, but C requires at least one argument before
+   the ellipsis, so we have a dummy one.  */
+
+extern void __emit_expression_range (int dummy, ...);
+
+int global;
+
+/* Simple expressions.  ************************************************/
+
+void test_literals (void)
+{
+  __emit_expression_range (0, 1066 ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 1066 );
+                               ^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, "hello" ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, "hello" );
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_variables (int param)
+{
+  __emit_expression_range (0, param ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, param );
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, global ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, global );
+                               ^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+#if 1
+
+void test_parentheses (int a, int b)
+{
+  __emit_expression_range (0, (a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) );
+                               ~~~^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, (a + b) * (a - b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) * (a - b) );
+                               ~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, !(a && b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !(a && b) );
+                               ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Postfix expressions.  ************************************************/
+
+void test_array_reference (int *arr)
+{
+  __emit_expression_range (0, arr[100] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, arr[100] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+}
+
+int test_function_call (int p, int q, int r)
+{
+  __emit_expression_range (0, test_function_call (p, q, r) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, test_function_call (p, q, r) );
+                               ~~~~~~~~~~~~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+  return 0;
+}
+
+struct test_struct
+{
+  int field;
+};
+
+int test_structure_references (struct test_struct *ptr)
+{
+  struct test_struct local;
+  local.field = 42;
+
+  __emit_expression_range (0, local.field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, local.field );
+                               ~~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, ptr->field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ptr->field );
+                               ~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+int test_postfix_incdec (int i)
+{
+  __emit_expression_range (0, i++ ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i++ );
+                               ~^~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, i-- ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i-- );
+                               ~^~
+   { dg-end-multiline-output "" } */
+}
+
+/* Unary operators.  ****************************************************/
+
+int test_prefix_incdec (int i)
+{
+  __emit_expression_range (0, ++i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ++i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, --i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, --i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_address_operator (void)
+{
+  __emit_expression_range (0, &global ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &global );
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_indirection (int *ptr)
+{
+  __emit_expression_range (0, *ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *ptr );
+                               ^~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_unary_minus (int i)
+{
+  __emit_expression_range (0, -i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, -i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_ones_complement (int i)
+{
+  __emit_expression_range (0, ~i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ~i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_negation (int flag)
+{
+  __emit_expression_range (0, !flag ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !flag );
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Casts.  ****************************************************/
+
+void test_cast (void *ptr)
+{
+  __emit_expression_range (0, (int *)ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (int *)ptr );
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, *(int *)0xdeadbeef ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *(int *)0xdeadbeef );
+                               ^~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* Binary operators.  *******************************************/
+
+void test_multiplicative_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs * rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs * rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs / rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs / rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs % rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs % rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_additive_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs + rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs + rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs - rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs - rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_shift_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs << rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs << rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >> rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >> rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_relational_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs < rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs < rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs > rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs > rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs <= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs <= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_equality_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs == rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs == rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs != rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs != rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_bitwise_binary_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs & rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs & rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs ^ rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs ^ rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs | rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs | rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs && rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs && rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs || rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs || rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Conditional operator.  *******************************************/
+
+void test_conditional_operators (int flag, int on_true, int on_false)
+{
+  __emit_expression_range (0, flag ? on_true : on_false ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, flag ? on_true : on_false );
+                               ~~~~~^~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Assignment expressions.  *******************************************/
+
+void test_assignment_expressions (int dest, int other)
+{
+  __emit_expression_range (0, dest = other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest = other );
+                               ~~~~~^~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest *= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest *= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest /= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest /= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest %= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest %= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest += other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest += other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest -= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest -= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest <<= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest <<= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest >>= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest >>= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest &= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest &= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest ^= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest ^= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest |= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest |= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Comma operator.  *******************************************/
+
+void test_comma_operator (int a, int b)
+{
+  __emit_expression_range (0, (a++, a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a++, a + b) );
+                               ~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Literals.  **************************************************/
+
+/* We can't test the ranges of literals directly, since the underlying
+   tree nodes don't retain a location.  However, we can test that they
+   have ranges during parsing by building compound expressions using
+   them, and verifying the ranges of the compound expressions.  */
+
+void test_string_literals (int i)
+{
+  __emit_expression_range (0, "foo"[i] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, "foo"[i] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, &"foo" "bar" ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &"foo" "bar" );
+                               ^~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Examples of non-trivial expressions.  ****************************/
+
+extern double sqrt (double x);
+
+void test_quadratic (double a, double b, double c)
+{
+  __emit_expression_range (0, b * b - 4 * a * c ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, b * b - 4 * a * c );
+                               ~~~~~~^~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0,
+     (-b + sqrt (b * b - 4 * a * c))
+     / (2 * a)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+      (-b + sqrt (b * b - 4 * a * c))
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      / (2 * a));
+      ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* C++-specific expresssions. ****************************************/
+
+void test_cp_literal_keywords (int a, int b)
+{
+  this; /* { dg-error "invalid use of 'this' in non-member function" } */
+/* { dg-begin-multiline-output "" }
+   this;
+   ^~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+class base {
+ public:
+  base ();
+  base (int i);
+  virtual ~base ();
+};
+class derived : public base { ~derived (); };
+
+void test_cp_casts (base *ptr)
+{
+  __emit_expression_range (0, dynamic_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dynamic_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, static_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, static_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, reinterpret_cast <int *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, reinterpret_cast <int *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, const_cast <base *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, const_cast <base *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_new (void)
+{
+  __emit_expression_range (0, ::new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ::new base);
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base);
+                               ^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base));
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base (42));
+                               ^~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base) (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base) (42));
+                               ^~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  /* TODO: placement new.  */
+}
+
+#endif
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
new file mode 100644
index 0000000..0e98702
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic_plugin_test_tree_expression_range.c
@@ -0,0 +1,106 @@
+/* This plugin verifies the source-code location ranges of
+   expressions, at the pre-gimplification tree stage.  */
+/* { dg-options "-O" } */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "cp/cp-tree.h"
+#include "stringpool.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "print-tree.h"
+
+int plugin_is_GPL_compatible;
+
+static void
+emit_warning (location_t loc)
+{
+  source_range src_range = get_range_from_loc (line_table, loc);
+  warning_at (loc, 0,
+	      "tree range %i:%i-%i:%i",
+	      LOCATION_LINE (src_range.m_start),
+	      LOCATION_COLUMN (src_range.m_start),
+	      LOCATION_LINE (src_range.m_finish),
+	      LOCATION_COLUMN (src_range.m_finish));
+}
+
+tree
+cb_walk_tree_fn (tree * tp, int * walk_subtrees,
+		 void * data ATTRIBUTE_UNUSED)
+{
+  if (TREE_CODE (*tp) != CALL_EXPR)
+    return NULL_TREE;
+
+  tree call_expr = *tp;
+  tree fn = CALL_EXPR_FN (call_expr);
+  if (TREE_CODE (fn) != ADDR_EXPR)
+    return NULL_TREE;
+  fn = TREE_OPERAND (fn, 0);
+  STRIP_LOCATION_EXPRS (fn);
+  if (TREE_CODE (fn) != FUNCTION_DECL)
+    return NULL_TREE;
+  if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fn)), "__emit_expression_range"))
+    return NULL_TREE;
+
+  /* Get arg 1; print it! */
+  tree arg = CALL_EXPR_ARG (call_expr, 1);
+
+#if 0
+  fprintf (stderr, "call_expr: %p\n", call_expr);
+  fprintf (stderr, "arg: %p\n", arg);
+  debug_tree (arg);
+#endif
+
+  emit_warning (EXPR_LOCATION (arg));
+
+  return NULL_TREE;
+}
+
+static void
+callback (void *gcc_data, void *user_data)
+{
+  tree fndecl = (tree)gcc_data;
+  walk_tree (&DECL_SAVED_TREE (fndecl), cb_walk_tree_fn, NULL, NULL);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  register_callback (plugin_name,
+		     PLUGIN_PRE_GENERICIZE,
+		     callback,
+		     NULL);
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
index 3ed1397..2266380 100644
--- a/gcc/testsuite/g++.dg/plugin/plugin.exp
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -62,7 +62,10 @@ set plugin_test_list [list \
     { dumb_plugin.c dumb-plugin-test-1.C } \
     { header_plugin.c header-plugin-test.C } \
     { decl_plugin.c decl-plugin-test.C } \
-    { def_plugin.c def-plugin-test.C } ]
+    { def_plugin.c def-plugin-test.C } \
+    { diagnostic_plugin_test_tree_expression_range.c \
+	  diagnostic-test-expressions-1.c } \
+]
 
 foreach plugin_test $plugin_test_list {
     # Replace each source file with its full-path name
diff --git a/gcc/tree.c b/gcc/tree.c
index d5a71a3..e7f4dcf 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13884,7 +13884,7 @@ nonnull_arg_p (const_tree arg)
 /* Given location LOC, strip away any packed range information
    or ad-hoc information.  */
 
-static location_t
+location_t
 get_pure_location (location_t loc)
 {
   if (IS_ADHOC_LOC (loc))
@@ -13914,20 +13914,20 @@ set_block (location_t loc, tree block)
   return COMBINE_LOCATION_DATA (line_table, pure_loc, src_range, block);
 }
 
-void
+location_t
 set_source_range (tree expr, location_t start, location_t finish)
 {
   source_range src_range;
   src_range.m_start = start;
   src_range.m_finish = finish;
-  set_source_range (expr, src_range);
+  return set_source_range (expr, src_range);
 }
 
-void
+location_t
 set_source_range (tree expr, source_range src_range)
 {
   if (!EXPR_P (expr))
-    return;
+    return UNKNOWN_LOCATION;
 
   location_t pure_loc = get_pure_location (EXPR_LOCATION (expr));
   location_t adhoc = COMBINE_LOCATION_DATA (line_table,
@@ -13935,6 +13935,21 @@ set_source_range (tree expr, source_range src_range)
 					    src_range,
 					    NULL);
   SET_EXPR_LOCATION (expr, adhoc);
+  return adhoc;
+}
+
+location_t
+make_location (location_t caret, location_t start, location_t finish)
+{
+  location_t pure_loc = get_pure_location (caret);
+  source_range src_range;
+  src_range.m_start = start;
+  src_range.m_finish = finish;
+  location_t combined_loc = COMBINE_LOCATION_DATA (line_table,
+						   pure_loc,
+						   src_range,
+						   NULL);
+  return combined_loc;
 }
 
 /* Return the name of combined function FN, for debugging purposes.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 41c0f7c..54164f1 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5337,6 +5337,7 @@ type_with_alias_set_p (const_tree t)
   return false;
 }
 
+extern location_t get_pure_location (location_t loc);
 extern location_t set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
@@ -5345,10 +5346,10 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
 
 extern bool nonnull_arg_p (const_tree);
 
-extern void
+extern location_t
 set_source_range (tree expr, location_t start, location_t finish);
 
-extern void
+extern location_t
 set_source_range (tree expr, source_range src_range);
 
 static inline source_range
@@ -5358,4 +5359,37 @@ get_decl_source_range (tree decl)
   return get_range_from_loc (line_table, loc);
 }
 
+extern location_t
+make_location (location_t caret, location_t start, location_t finish);
+
+/* FIXME.  */
+inline location_t
+get_start (location_t loc)
+{
+  source_range src_range = get_range_from_loc (line_table, loc);
+  return src_range.m_start;
+}
+
+/* FIXME.  */
+inline location_t
+get_finish (location_t loc)
+{
+  source_range src_range = get_range_from_loc (line_table, loc);
+  return src_range.m_finish;
+}
+
+/* FIXME.  */
+inline location_t
+get_expr_start (tree expr)
+{
+  return get_start (EXPR_LOCATION (expr));
+}
+
+/* FIXME.  */
+inline location_t
+get_expr_finish (tree expr)
+{
+  return get_finish (EXPR_LOCATION (expr));
+}
+
 #endif  /* GCC_TREE_H  */

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-24 11:08                     ` David Malcolm
@ 2015-11-24 11:50                       ` Richard Biener
  2015-11-24 12:15                       ` Marek Polacek
  1 sibling, 0 replies; 40+ messages in thread
From: Richard Biener @ 2015-11-24 11:50 UTC (permalink / raw)
  To: David Malcolm; +Cc: Jason Merrill, Marek Polacek, Jakub Jelinek, GCC Patches

On Tue, Nov 24, 2015 at 11:58 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> On Tue, 2015-11-24 at 10:40 +0100, Richard Biener wrote:
>> On Mon, Nov 23, 2015 at 8:25 PM, Jason Merrill <jason@redhat.com> wrote:
>> > On 11/23/2015 12:07 PM, Marek Polacek wrote:
>> >>
>> >> On Mon, Nov 23, 2015 at 05:57:54PM +0100, Jakub Jelinek wrote:
>> >>>
>> >>> On Mon, Nov 23, 2015 at 11:53:40AM -0500, David Malcolm wrote:
>> >>>>
>> >>>> Does the following look like the kind of thing you had in mind?  (just
>> >>>> the tree.def part for now).   Presumably usable for both lvalues and
>> >>>> rvalues, where the thing it wraps is what's important.  It merely exists
>> >>>> to add an EXPR_LOCATION, for a usage of the wrapped thing.
>> >>>
>> >>>
>> >>> Yes, but please see with Jason, Richard and perhaps others if they are ok
>> >>> with that too before spending too much time in that direction.
>> >>> All occurrences of it would have to be folded away during the
>> >>> gimplification
>> >>> at latest, this shouldn't be something we use in the middle-end.
>> >>
>> >>
>> >> I'd expect LOCATION_EXPR be defined in c-family/c-common.def, not
>> >> tree.def.
>> >> And I'd think it shouldn't survive genericizing, thus never leak into the
>> >> ME.
>> >
>> >
>> > Makes sense.
>
> FWIW, attached is a work-in-progress patch for the LOCATION_EXPR
> approach.  This one
> * adds LOCATION_EXPR to cp/cp-tree.def, rather than to c-common.def (my
> thinking being that if it's only being used in the C++ FE, make it
> specific to it)
> * LOCATION_EXPR wrapper nodes are created for VAR_DECL, PARM_DECL (I'm
> not sure if I want to create them for STRING_CST)
> * strips them out in cp_genericize_r
> * verifies their absence in cp_gimplify_expr via a gcc_unreachable
> * lots of use of STRIP_LOCATION_EXPRS around places which expect a
> certain kind of node.  This feels like a game of whack-a-mole.  I've got
> increasing amounts of the C++ stdlib to parse, but am running into
> various folding issues:
>
> x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42: error:
> ‘(std::size_t)0’ is not a constant expression
>        static const size_t _S_alignment = 0;
>                                           ^
>
> which doesn't seem to be easily fixable; I'd already put a cp_fully_fold
> into cp/constexpr.c:verify_constant, but this is:
>
> (gdb) call debug_tree (t)
>  <nop_expr 0x7fffef03d580
>     type <integer_type 0x7ffff1632f18 size_t unsigned type_6 DI
>         size <integer_cst 0x7ffff1891e58 constant 64>
>         unit size <integer_cst 0x7ffff1891e70 constant 8>
>         align 64 symtab 0 alias set -1 canonical type 0x7ffff18959d8
> precision 64 min <integer_cst 0x7ffff18b3138 0> max <integer_cst
> 0x7ffff18a35e0 18446744073709551615>>
>     constant
>     arg 0 <location_expr 0x7fffef03d4e0
>         type <integer_type 0x7ffff18957e0 int asm_written public type_6
> SI
>             size <integer_cst 0x7ffff18b30a8 constant 32>
>             unit size <integer_cst 0x7ffff18b30c0 constant 4>
>             align 32 symtab -243642208 alias set -1 canonical type
> 0x7ffff18957e0 precision 32 min <integer_cst 0x7ffff18b3060 -2147483648>
> max <integer_cst 0x7ffff18b3078 2147483647>
>             pointer_to_this <pointer_type 0x7ffff18b7930>>
>         constant
>         arg 0 <integer_cst 0x7ffff18b31f8 constant 0>
>         /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 start: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 finish: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42>
>     /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 start: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42 finish: /home/david/coding-3/gcc-git-rich-errors/build-no-cloog/x86_64-pc-linux-gnu/libstdc++-v3/include/type_traits:2053:42>
>
> which isn't folded here by cp_fully_fold since we're in
> processing_template_decl; STRIP_NOPS etc can't do it since LOCATION_EXPR
> is frontend-specific.
>
> Any thoughts on how to work around this?
>
>
>> OTOH then the FEs need to strip trees of it if they ever want to use
>> sth from fold-const.c for example.  NON_LVALUE_EXPR is not
>> in c-family/c-common.def either... (and conveniently stripped with
>> STRIP_NOPS and friends).
>
> Just to clarify, do you mean "NON_LVALUE_EXPR is *not* conveniently
> stripped by STRIP_NOPS and friends"?

No, it _is_ conveniently stripped because it's a middle-end tree code.
All I wanted to say is that if you can make it work with a lang-tree-code
more power to you but I'd expect it to me much easier to use a middle-end
one if not just because of STRIP_NOPS and fold.

>  As far as I can tell, STRIP_NOPS
> etc are all frontend-independent, hence frontend-specific tree types
> can't be handled there.

Yes (we'd definitely not want a langhook here ;))

>> Ok, I _would_ like to move NON_LVALUE_EXPR to the C frontend
>> family as well...  so I guess that would be a nice starting project
>> and if you can make that work you can add LOCATION_EXPR to
>> the C family only.
>
> At a higher level, I'm nervous about feature-creep here, relative to the
> v2 patch; that one (mostly) worked, without creating a new tree type, in
> that it captured the locations (and thus ranges) for the leaf nodes of
> the parse tree for just long enough to let them be used when building
> more interesting expressions, which is where the range information is
> useful.

I think at this stage a new tree code isn't appropriate.

Richard.

> Dave

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH/RFC] C++ FE: expression ranges (v2)
  2015-11-24 11:08                     ` David Malcolm
  2015-11-24 11:50                       ` Richard Biener
@ 2015-11-24 12:15                       ` Marek Polacek
  1 sibling, 0 replies; 40+ messages in thread
From: Marek Polacek @ 2015-11-24 12:15 UTC (permalink / raw)
  To: David Malcolm; +Cc: Richard Biener, Jason Merrill, Jakub Jelinek, GCC Patches

On Tue, Nov 24, 2015 at 05:58:30AM -0500, David Malcolm wrote:
> +/* Language-dependent macro for stripping away location wrapper nodes.  */
> +
> +#define STRIP_LOCATION_EXPRS(EXP) \
> +  while (TREE_CODE (EXP) == LOCATION_EXPR) \
> +    (EXP) = TREE_OPERAND ((EXP), 0)

This BTW implies that we might have a LOCATION_EXPR wrapped in another
LOCATION_EXPR, but I don't quite see how that could be useful?

	Marek

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH/RFC 0/2] C++ FE: expression ranges (v3)
  2015-11-21  8:22     ` Jason Merrill
  2015-11-21  8:22       ` Jakub Jelinek
@ 2015-11-25 20:32       ` David Malcolm
  2015-11-25 20:32         ` [PATCH 1/2] RFC: C++: attempt to provide location_t in more places David Malcolm
  2015-11-25 20:38         ` [PATCH 2/2] RFC: C++ FE: expression ranges (work in progress) v3 David Malcolm
  1 sibling, 2 replies; 40+ messages in thread
From: David Malcolm @ 2015-11-25 20:32 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

On Sat, 2015-11-21 at 02:16 -0500, Jason Merrill wrote:
> On 11/19/2015 03:46 PM, Jason Merrill wrote:
> > On 11/15/2015 12:01 AM, David Malcolm wrote:
> >> As with the C frontend, there's an issue with tree nodes that
> >> don't have locations: VAR_DECL, INTEGER_CST, etc:
> >>
> >>    int test (int foo)
> >>    {
> >>      return foo * 100;
> >>             ^^^   ^^^
> >>    }
> >>
> >> where we'd like to access the source spelling ranges of the expressions
> >> during parsing, so that we can use them when reporting parser errors.
> >
> > Hmm, I had been thinking to address this in the C++ front end by
> > wrapping uses in another tree: NOP_EXPR for rvalues, VIEW_CONVERT_EXPR
> > for lvalues.

As seen down-thread, I started attempting something like this, with
a new tree code, but Richi said:
> I think at this stage a new tree code isn't appropriate.
which presumably rules that approach out for gcc 6.

I'm still hoping to get expression ranges for C++ into gcc 6, hence
am still experimenting with the cp_expr approach; here is v3 of the
patch - though I appreciate we're well past stage 1.

> On the other hand, my direction seems likely to cause more issues, 
> especially with code that doesn't yet know how to handle 
> VIEW_CONVERT_EXPR, and could create ambiguity with explicit conversions. 
>   So I guess your approach seems reasonable.
> 
> What is the memory consumption impact of this change?

kdecore.cc ggc usage in KB, relative to a control build of r230562
-O0: Mem max: 659141.000 -> 659141.000: no change
-O1: Mem max: 955883.000 -> 955891.000: 1.0000x larger
-O2: Mem max: 1168490.000 -> 1184881.000: 1.0140x larger
-O3: Mem max: 1269955.000 -> 1286526.000: 1.0130x larger
-Os: Mem max: 875913.000 -> 884114.000: 1.0094x larger

It doesn't appear to significantly affect compile-time for that test.

> > Also, in cp_parser_new_expression I attempted to generate meaningful
> > ranges e.g.:
> >
> >   int *foo = new int[100];
> >              ^~~~~~~~~~~~
> >
> > but it seems to be hard to do this, due to the trailing optional
> > components; I found myself wanting to ask the lexer for the last
> > token it consumed (in particular, I'm interested in the
> > location_t of that token).  Is this a reasonable thing to add to the
> > lexer?
> 
> cp_lexer_previous_token seems like what you want.

Thanks.  I've used in it the latest version of the patch.

> > -      return cp_build_unary_op (code, arg1, candidates != 0, complain);
> > +      {
> > +	tree result = cp_build_unary_op (code, arg1, candidates != 0, complain);
> > +	protected_set_expr_location (result, loc);
> 
> I'd much rather pass loc into cp_build_unary_op.  At least add a FIXME 
> to that effect.  Likewise in the other places you call *build* and then 
> set the loc.

I've attempted this.  In the following I've split the patch into two:
an initial patch which adds location_t params to various tree-building
functions in the C++ FE, and then the followup patch to use this.

However, as I note in the patch themself, I'm not sure I've done it
correctly.  For the places where there's no location_t available, it's
never clear to me if I should be using input_location or
UNKNOWN_LOCATION, and there are some changes to where diagnostics
are emitted, which shows up as regressions.  In particular it's not
clear to me how locations are meant to interact with templates (and
I believe that to be the biggest area of risk with the patch).

> > +#if 0
> > +    /* FIXME: various assertions can be put in here when debugging,
> > +       for tracking down where location information gets thrown
> > +       away (during a trip through a purely "tree" value).  */
> > +    if (m_value && m_value != error_mark_node)
> > +      {
> > +	if (TREE_CODE (m_value) == FUNCTION_DECL)
> > +	  return; // for now
> > +	gcc_assert (CAN_HAVE_LOCATION_P (m_value));
> > +	//gcc_assert (m_loc != UNKNOWN_LOCATION);
> 
> Yeah, I don't think you want to assert on UNKNOWN_LOCATION; some code 
> does not and should not have an associated location.  In particular, 
> cleanups.

I've eliminated that #if 0 code in the latest version of the patch.

 
> >  Build the assignment expression.  Its default
> > -		 location is the location of the '=' token.  */
> > +		 location:
> > +		   LHS = RHS
> > +		   ~~~~^~~~~
> > +		 is the location of the '=' token as the
> > +		 caret, ranging from the start of the lhs to the
> > +		 end of the rhs.  */
> >  	      saved_input_location = input_location;
> > +	      loc = make_location (loc,
> > +				   expr.get_start (),
> > +				   rhs.get_finish ());
> >  	      input_location = loc;
> >  	      expr = build_x_modify_expr (loc, expr,
> >  					  assignment_operator,
> >  					  rhs,
> >  					  complain_flags (decltype_p));
> > +	      protected_set_expr_location (expr, loc);
> >  	      input_location = saved_input_location;
> 
> Do we still need to mess with input_location here?  If so, please add a 
> FIXME explaining what still needs to be fixed.

I've eliminated that save/restore of input_location, using the
approach described above.

Thanks.

Dave

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 1/2] RFC: C++: attempt to provide location_t in more places
  2015-11-25 20:32       ` [PATCH/RFC 0/2] C++ FE: expression ranges (v3) David Malcolm
@ 2015-11-25 20:32         ` David Malcolm
  2015-11-25 21:33           ` Jason Merrill
  2015-11-25 20:38         ` [PATCH 2/2] RFC: C++ FE: expression ranges (work in progress) v3 David Malcolm
  1 sibling, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-11-25 20:32 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

This patch avoids the need for calls to protected_set_expr_location
in the followup patch by adding location_t params to the following
functions:
  - build_new
  - cp_build_indirect_ref
  - cp_build_unary_op
  - cp_build_c_cast
  - cp_build_modify_expr

It's not clear to me whether I should be passing in UNKNOWN_LOCATION
or input_location to the various functions.

cp_build_unary_op used input_location in various places internally,
so I've passed that in wherever there isn't a better value.

Bootstraps (on x86_64-pc-linux-gnu), but regresses some tests, due to
changes in locations at which diagnostics are emitted:

  c-c++-common/cilk-plus/CK/cilk_for_errors.c
  c-c++-common/cilk-plus/PS/for1.c
  c-c++-common/gomp/pr59073.c
  g++.dg/cpp0x/nsdmi-template14.C
  g++.dg/gomp/for-1.C
  g++.dg/gomp/pr39495-2.C
  g++.dg/init/new38.C
  g++.dg/warn/Wconversion-real-integer2.C
  g++.dg/warn/pr35635.C

Is the overall idea sound?
Would this be better split up into separate patches by function?

Partial ChangeLog follows

gcc/cp/ChangeLog:
	* call.c (build_new_op_1): Pass "loc" to calls to
	cp_build_modify_expr, cp_build_indirect_ref, cp_build_unary_op.
	(build_over_call): Pass UNKNOWN_LOCATION to calls to
	cp_build_indirect_ref.
	(build_java_interface_fn_ref): Likewise.
	* class.c (build_base_path): Likewise, though it appears I used
	input_location in one place.
	(build_simple_base_path): Likewise.
	(build_vfn_ref): Likewise.
	* cp-tree.h (build_new): Add location_t param.
	(cp_build_indirect_ref): Likewise.
	(cp_build_unary_op): Likewise.
	(cp_build_c_cast): Likewise.
	(cp_build_modify_expr): Likewise.
	(build_address_loc): New decl.
	(build_nop_loc): New decl.
	* decl.c (start_preparsed_function): Pass UNKNOWN_LOCATION to call
	to cp_build_indirect_ref.
	* decl2.c (set_guard): Pass input_location to call to
	cp_build_modify_expr.
	(one_static_initialization_or_destruction): Likewise for calls to
	cp_build_unary_op.
	(handle_tls_init): Likewise for calls to cp_build_unary_op and
	cp_build_modify_expr.
	* except.c (expand_start_catch_block): Likewise for call to
	build_indirect_ref.
	(build_throw): Likewise.
	* init.c (perform_member_init): Likewise for call to
	cp_build_modify_expr.
	(emit_mem_initializers): Pass UNKNOWN_LOCATION to call to
	cp_build_indirect_ref.
	(expand_virtual_init): Likewise for two calls; pass input_location
	to call to cp_build_modify_expr.
	(build_raw_new_expr): Add location_t param "loc", use it to change
	a build4 to a build4_loc.
	(build_new_1): Pass input_location to three calls to
	cp_build_indirect_ref, UNKNOWN_LOCATION to two others.
	(build_new): Add location_t param "loc"; pass it to calls to
	build_raw_new_expr, and to change a build1 to a build1_loc.
	[...etc...]
---
 gcc/cp/call.c      |  28 ++++---
 gcc/cp/class.c     |  19 +++--
 gcc/cp/cp-tree.h   |  19 +++--
 gcc/cp/decl.c      |   3 +-
 gcc/cp/decl2.c     |  13 +--
 gcc/cp/except.c    |   6 +-
 gcc/cp/init.c      |  99 ++++++++++++----------
 gcc/cp/method.c    |   4 +-
 gcc/cp/parser.c    |  17 ++--
 gcc/cp/pt.c        |   5 +-
 gcc/cp/rtti.c      |  11 +--
 gcc/cp/semantics.c |  11 ++-
 gcc/cp/tree.c      |   6 +-
 gcc/cp/typeck.c    | 238 +++++++++++++++++++++++++++++++----------------------
 gcc/cp/typeck2.c   |  10 ++-
 15 files changed, 287 insertions(+), 202 deletions(-)

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 8cdda62..435ac7a 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5715,10 +5715,10 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
   switch (code)
     {
     case MODIFY_EXPR:
-      return cp_build_modify_expr (arg1, code2, arg2, complain);
+      return cp_build_modify_expr (loc, arg1, code2, arg2, complain);
 
     case INDIRECT_REF:
-      return cp_build_indirect_ref (arg1, RO_UNARY_STAR, complain);
+      return cp_build_indirect_ref (loc, arg1, RO_UNARY_STAR, complain);
 
     case TRUTH_ANDIF_EXPR:
     case TRUTH_ORIF_EXPR:
@@ -5768,14 +5768,15 @@ build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
     case REALPART_EXPR:
     case IMAGPART_EXPR:
     case ABS_EXPR:
-      return cp_build_unary_op (code, arg1, candidates != 0, complain);
+      return cp_build_unary_op (loc, code, arg1, candidates != 0, complain);
 
     case ARRAY_REF:
       return cp_build_array_ref (input_location, arg1, arg2, complain);
 
     case MEMBER_REF:
-      return build_m_component_ref (cp_build_indirect_ref (arg1, RO_ARROW_STAR, 
-                                                           complain), 
+      return build_m_component_ref (cp_build_indirect_ref (loc, arg1,
+							   RO_ARROW_STAR,
+                                                           complain),
                                     arg2, complain);
 
       /* The caller will deal with these.  */
@@ -7551,7 +7552,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       if (targ)
 	arg = targ;
       else
-	arg = cp_build_indirect_ref (arg, RO_NULL, complain);
+	arg = cp_build_indirect_ref (UNKNOWN_LOCATION, arg, RO_NULL, complain);
 
       /* [class.copy]: the copy constructor is implicitly defined even if
 	 the implementation elided its use.  */
@@ -7579,8 +7580,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	       || (TREE_CODE (arg) == TARGET_EXPR
 		   && !unsafe_copy_elision_p (fa, arg)))
 	{
-	  tree to = stabilize_reference (cp_build_indirect_ref (fa, RO_NULL,
-								complain));
+	  tree to
+	    = stabilize_reference (cp_build_indirect_ref (UNKNOWN_LOCATION,
+							  fa, RO_NULL,
+							  complain));
 
 	  val = build2 (INIT_EXPR, DECL_CONTEXT (fn), to, arg);
 	  return val;
@@ -7591,7 +7594,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	   && !DECL_DELETED_FN (fn))
     {
       tree to = stabilize_reference
-	(cp_build_indirect_ref (argarray[0], RO_NULL, complain));
+	(cp_build_indirect_ref (UNKNOWN_LOCATION, argarray[0], RO_NULL,
+				complain));
       tree type = TREE_TYPE (to);
       tree as_base = CLASSTYPE_AS_BASE (type);
       tree arg = argarray[1];
@@ -7606,7 +7610,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	}
       else if (tree_int_cst_equal (TYPE_SIZE (type), TYPE_SIZE (as_base)))
 	{
-	  arg = cp_build_indirect_ref (arg, RO_NULL, complain);
+	  arg = cp_build_indirect_ref (UNKNOWN_LOCATION, arg, RO_NULL,
+				       complain);
 	  val = build2 (MODIFY_EXPR, TREE_TYPE (to), to, arg);
 	}
       else
@@ -7805,7 +7810,8 @@ build_java_interface_fn_ref (tree fn, tree instance)
 
   /* Look up the pointer to the runtime java.lang.Class object for `instance'.
      This is the first entry in the vtable.  */
-  klass_ref = build_vtbl_ref (cp_build_indirect_ref (instance, RO_NULL, 
+  klass_ref = build_vtbl_ref (cp_build_indirect_ref (UNKNOWN_LOCATION,
+						     instance, RO_NULL,
                                                      tf_warning_or_error),
 			      integer_zero_node);
 
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 216a301..b9728e5 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -395,7 +395,7 @@ build_base_path (enum tree_code code,
 	 interesting to the optimizers anyway.  */
       && !has_empty)
     {
-      expr = cp_build_indirect_ref (expr, RO_NULL, complain);
+      expr = cp_build_indirect_ref (UNKNOWN_LOCATION, expr, RO_NULL, complain);
       expr = build_simple_base_path (expr, binfo);
       if (rvalue)
 	expr = move (expr);
@@ -422,7 +422,8 @@ build_base_path (enum tree_code code,
 	  t = TREE_TYPE (TYPE_VFIELD (current_class_type));
 	  t = build_pointer_type (t);
 	  v_offset = fold_convert (t, current_vtt_parm);
-	  v_offset = cp_build_indirect_ref (v_offset, RO_NULL, complain);
+	  v_offset = cp_build_indirect_ref (UNKNOWN_LOCATION, v_offset, RO_NULL,
+					    complain);
 	}
       else
 	{
@@ -434,7 +435,8 @@ build_base_path (enum tree_code code,
 	      if (t == NULL_TREE)
 		t = expr;
 	    }
-	  v_offset = build_vfield_ref (cp_build_indirect_ref (t, RO_NULL,
+	  v_offset = build_vfield_ref (cp_build_indirect_ref (input_location,
+							      t, RO_NULL,
 							      complain),
 	  TREE_TYPE (TREE_TYPE (expr)));
 	}
@@ -446,7 +448,8 @@ build_base_path (enum tree_code code,
       v_offset = build1 (NOP_EXPR,
 			 build_pointer_type (ptrdiff_type_node),
 			 v_offset);
-      v_offset = cp_build_indirect_ref (v_offset, RO_NULL, complain);
+      v_offset = cp_build_indirect_ref (UNKNOWN_LOCATION, v_offset, RO_NULL,
+					complain);
       TREE_CONSTANT (v_offset) = 1;
 
       offset = convert_to_integer (ptrdiff_type_node,
@@ -488,7 +491,7 @@ build_base_path (enum tree_code code,
  indout:
   if (!want_pointer)
     {
-      expr = cp_build_indirect_ref (expr, RO_NULL, complain);
+      expr = cp_build_indirect_ref (UNKNOWN_LOCATION, expr, RO_NULL, complain);
       if (rvalue)
 	expr = move (expr);
     }
@@ -524,7 +527,8 @@ build_simple_base_path (tree expr, tree binfo)
 	 in the back end.  */
       temp = unary_complex_lvalue (ADDR_EXPR, expr);
       if (temp)
-	expr = cp_build_indirect_ref (temp, RO_NULL, tf_warning_or_error);
+	expr = cp_build_indirect_ref (UNKNOWN_LOCATION, temp, RO_NULL,
+				      tf_warning_or_error);
 
       return expr;
     }
@@ -717,7 +721,8 @@ build_vfn_ref (tree instance_ptr, tree idx)
 {
   tree aref;
 
-  aref = build_vtbl_ref_1 (cp_build_indirect_ref (instance_ptr, RO_NULL,
+  aref = build_vtbl_ref_1 (cp_build_indirect_ref (UNKNOWN_LOCATION,
+						  instance_ptr, RO_NULL,
                                                   tf_warning_or_error), 
                            idx);
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 160bf1e..4878161 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5895,8 +5895,9 @@ extern tree get_nsdmi				(tree, bool);
 extern tree build_offset_ref			(tree, tree, bool,
 						 tsubst_flags_t);
 extern tree throw_bad_array_new_length		(void);
-extern tree build_new				(vec<tree, va_gc> **, tree, tree,
-						 vec<tree, va_gc> **, int,
+extern tree build_new				(location_t,
+						 vec<tree, va_gc> **, tree,
+						 tree, vec<tree, va_gc> **, int,
                                                  tsubst_flags_t);
 extern tree get_temp_regvar			(tree, tree);
 extern tree build_vec_init			(tree, tree, tree, bool, int,
@@ -6558,7 +6559,7 @@ extern tree finish_class_member_access_expr     (tree, tree, bool,
 						 tsubst_flags_t);
 extern tree build_x_indirect_ref		(location_t, tree,
 						 ref_operator, tsubst_flags_t);
-extern tree cp_build_indirect_ref		(tree, ref_operator,
+extern tree cp_build_indirect_ref		(location_t, tree, ref_operator,
                                                  tsubst_flags_t);
 extern tree build_array_ref			(location_t, tree, tree);
 extern tree cp_build_array_ref			(location_t, tree, tree,
@@ -6579,8 +6580,8 @@ extern tree build_x_unary_op			(location_t,
 						 enum tree_code, tree,
                                                  tsubst_flags_t);
 extern tree cp_build_addr_expr			(tree, tsubst_flags_t);
-extern tree cp_build_unary_op                   (enum tree_code, tree, int, 
-                                                 tsubst_flags_t);
+extern tree cp_build_unary_op                   (location_t, enum tree_code,
+						 tree, int,  tsubst_flags_t);
 extern tree unary_complex_lvalue		(enum tree_code, tree);
 extern tree build_x_conditional_expr		(location_t, tree, tree, tree, 
                                                  tsubst_flags_t);
@@ -6596,11 +6597,13 @@ extern tree build_static_cast			(tree, tree, tsubst_flags_t);
 extern tree build_reinterpret_cast		(tree, tree, tsubst_flags_t);
 extern tree build_const_cast			(tree, tree, tsubst_flags_t);
 extern tree build_c_cast			(location_t, tree, tree);
-extern tree cp_build_c_cast			(tree, tree, tsubst_flags_t);
+extern tree cp_build_c_cast			(location_t, tree, tree,
+						 tsubst_flags_t);
 extern tree build_x_modify_expr			(location_t, tree,
 						 enum tree_code, tree,
 						 tsubst_flags_t);
-extern tree cp_build_modify_expr		(tree, enum tree_code, tree,
+extern tree cp_build_modify_expr		(location_t, tree,
+						 enum tree_code, tree,
 						 tsubst_flags_t);
 extern tree convert_for_initialization		(tree, tree, tree, int,
 						 impl_conv_rhs, tree, int,
@@ -6638,7 +6641,9 @@ extern tree build_x_vec_perm_expr               (location_t,
 extern tree build_simple_component_ref		(tree, tree);
 extern tree build_ptrmemfunc_access_expr	(tree, tree);
 extern tree build_address			(tree);
+extern tree build_address_loc			(location_t, tree);
 extern tree build_nop				(tree, tree);
+extern tree build_nop_loc			(location_t, tree, tree);
 extern tree non_reference			(tree);
 extern tree lookup_anon_field			(tree, tree);
 extern bool invalid_nonstatic_memfn_p		(location_t, tree,
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 675342e..022300b 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -13908,7 +13908,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
       gcc_assert (TYPE_PTR_P (TREE_TYPE (t)));
 
       cp_function_chain->x_current_class_ref
-	= cp_build_indirect_ref (t, RO_NULL, tf_warning_or_error);
+	= cp_build_indirect_ref (UNKNOWN_LOCATION, t, RO_NULL,
+				 tf_warning_or_error);
       /* Set this second to avoid shortcut in cp_build_indirect_ref.  */
       cp_function_chain->x_current_class_ptr = t;
 
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 47c9ec9..86143b8 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -3139,7 +3139,7 @@ set_guard (tree guard)
   guard_init = integer_one_node;
   if (!same_type_p (TREE_TYPE (guard_init), TREE_TYPE (guard)))
     guard_init = fold_convert (TREE_TYPE (guard), guard_init);
-  return cp_build_modify_expr (guard, NOP_EXPR, guard_init, 
+  return cp_build_modify_expr (input_location, guard, NOP_EXPR, guard_init,
 			       tf_warning_or_error);
 }
 
@@ -3734,7 +3734,8 @@ one_static_initialization_or_destruction (tree decl, tree init, bool initp)
 	guard_cond
 	  = cp_build_binary_op (input_location,
 				EQ_EXPR,
-				cp_build_unary_op (PREINCREMENT_EXPR,
+				cp_build_unary_op (input_location,
+						   PREINCREMENT_EXPR,
 						   guard,
 						   /*noconvert=*/1,
 						   tf_warning_or_error),
@@ -3744,7 +3745,8 @@ one_static_initialization_or_destruction (tree decl, tree init, bool initp)
 	guard_cond
 	  = cp_build_binary_op (input_location,
 				EQ_EXPR,
-				cp_build_unary_op (PREDECREMENT_EXPR,
+				cp_build_unary_op (input_location,
+						   PREDECREMENT_EXPR,
 						   guard,
 						   /*noconvert=*/1,
 						   tf_warning_or_error),
@@ -4318,10 +4320,11 @@ handle_tls_init (void)
   start_preparsed_function (fn, NULL_TREE, SF_PRE_PARSED);
   tree body = begin_function_body ();
   tree if_stmt = begin_if_stmt ();
-  tree cond = cp_build_unary_op (TRUTH_NOT_EXPR, guard, false,
+  tree cond = cp_build_unary_op (input_location, TRUTH_NOT_EXPR, guard, false,
 				 tf_warning_or_error);
   finish_if_stmt_cond (cond, if_stmt);
-  finish_expr_stmt (cp_build_modify_expr (guard, NOP_EXPR, boolean_true_node,
+  finish_expr_stmt (cp_build_modify_expr (input_location, guard, NOP_EXPR,
+					  boolean_true_node,
 					  tf_warning_or_error));
   for (; vars; vars = TREE_CHAIN (vars))
     {
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 9b2450d..a581c3c 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -501,7 +501,8 @@ expand_start_catch_block (tree decl)
 		    fold_build1_loc (input_location,
 				     NEGATE_EXPR, sizetype,
 				     TYPE_SIZE_UNIT (TREE_TYPE (exp))));
-      exp = cp_build_indirect_ref (exp, RO_NULL, tf_warning_or_error);
+      exp = cp_build_indirect_ref (input_location, exp, RO_NULL,
+				   tf_warning_or_error);
       initialize_handler_parm (decl, exp);
       return type;
     }
@@ -818,7 +819,8 @@ build_throw (tree exp)
       CLEANUP_EH_ONLY (allocate_expr) = 1;
 
       object = build_nop (build_pointer_type (temp_type), ptr);
-      object = cp_build_indirect_ref (object, RO_NULL, tf_warning_or_error);
+      object = cp_build_indirect_ref (UNKNOWN_LOCATION, object, RO_NULL,
+				      tf_warning_or_error);
 
       /* And initialize the exception object.  */
       if (CLASS_TYPE_P (temp_type))
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 5ecf9fb..fffd203 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -793,7 +793,8 @@ perform_member_init (tree member, tree init)
 						tf_warning_or_error);
 
       if (init)
-	finish_expr_stmt (cp_build_modify_expr (decl, INIT_EXPR, init,
+	finish_expr_stmt (cp_build_modify_expr (input_location, decl,
+						INIT_EXPR, init,
 						tf_warning_or_error));
     }
 
@@ -1153,7 +1154,8 @@ emit_mem_initializers (tree mem_inits)
 	  base_addr = build_base_path (PLUS_EXPR, current_class_ptr,
 				       subobject, 1, tf_warning_or_error);
 	  expand_aggr_init_1 (subobject, NULL_TREE,
-			      cp_build_indirect_ref (base_addr, RO_NULL,
+			      cp_build_indirect_ref (UNKNOWN_LOCATION,
+						     base_addr, RO_NULL,
                                                      tf_warning_or_error),
 			      arguments,
 			      flags,
@@ -1232,7 +1234,8 @@ expand_virtual_init (tree binfo, tree decl)
       /* Compute the value to use, when there's a VTT.  */
       vtt_parm = current_vtt_parm;
       vtbl2 = fold_build_pointer_plus (vtt_parm, vtt_index);
-      vtbl2 = cp_build_indirect_ref (vtbl2, RO_NULL, tf_warning_or_error);
+      vtbl2 = cp_build_indirect_ref (UNKNOWN_LOCATION, vtbl2, RO_NULL,
+				     tf_warning_or_error);
       vtbl2 = convert (TREE_TYPE (vtbl), vtbl2);
 
       /* The actual initializer is the VTT value only in the subobject
@@ -1247,15 +1250,16 @@ expand_virtual_init (tree binfo, tree decl)
     }
 
   /* Compute the location of the vtpr.  */
-  vtbl_ptr = build_vfield_ref (cp_build_indirect_ref (decl, RO_NULL, 
+  vtbl_ptr = build_vfield_ref (cp_build_indirect_ref (UNKNOWN_LOCATION, decl,
+						      RO_NULL,
                                                       tf_warning_or_error),
 			       TREE_TYPE (binfo));
   gcc_assert (vtbl_ptr != error_mark_node);
 
   /* Assign the vtable to the vptr.  */
   vtbl = convert_force (TREE_TYPE (vtbl_ptr), vtbl, 0, tf_warning_or_error);
-  finish_expr_stmt (cp_build_modify_expr (vtbl_ptr, NOP_EXPR, vtbl,
-					  tf_warning_or_error));
+  finish_expr_stmt (cp_build_modify_expr (input_location, vtbl_ptr, NOP_EXPR,
+					  vtbl, tf_warning_or_error));
 }
 
 /* If an exception is thrown in a constructor, those base classes already
@@ -2135,8 +2139,8 @@ decl_constant_value (tree decl)
    creates and returns a NEW_EXPR.  */
 
 static tree
-build_raw_new_expr (vec<tree, va_gc> *placement, tree type, tree nelts,
-		    vec<tree, va_gc> *init, int use_global_new)
+build_raw_new_expr (location_t loc, vec<tree, va_gc> *placement, tree type,
+		    tree nelts, vec<tree, va_gc> *init, int use_global_new)
 {
   tree init_list;
   tree new_expr;
@@ -2152,9 +2156,9 @@ build_raw_new_expr (vec<tree, va_gc> *placement, tree type, tree nelts,
   else
     init_list = build_tree_list_vec (init);
 
-  new_expr = build4 (NEW_EXPR, build_pointer_type (type),
-		     build_tree_list_vec (placement), type, nelts,
-		     init_list);
+  new_expr = build4_loc (loc, NEW_EXPR, build_pointer_type (type),
+			 build_tree_list_vec (placement), type, nelts,
+			 init_list);
   NEW_EXPR_USE_GLOBAL (new_expr) = use_global_new;
   TREE_SIDE_EFFECTS (new_expr) = 1;
 
@@ -2985,7 +2989,8 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
 						alloc_node, cookie_ptr);
       size_ptr_type = build_pointer_type (sizetype);
       cookie_ptr = fold_convert (size_ptr_type, cookie_ptr);
-      cookie = cp_build_indirect_ref (cookie_ptr, RO_NULL, complain);
+      cookie = cp_build_indirect_ref (input_location, cookie_ptr, RO_NULL,
+				      complain);
 
       cookie_expr = build2 (MODIFY_EXPR, sizetype, cookie, nelts);
 
@@ -2997,7 +3002,8 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
 						NEGATE_EXPR, sizetype,
 						size_in_bytes (sizetype)));
 
-	  cookie = cp_build_indirect_ref (cookie_ptr, RO_NULL, complain);
+	  cookie = cp_build_indirect_ref (input_location, cookie_ptr, RO_NULL,
+					  complain);
 	  cookie = build2 (MODIFY_EXPR, sizetype, cookie,
 			   size_in_bytes (elt_type));
 	  cookie_expr = build2 (COMPOUND_EXPR, TREE_TYPE (cookie_expr),
@@ -3043,7 +3049,8 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
 	     the initializer anyway since we're going to throw it away and
 	     rebuild it at instantiation time, so just build up a single
 	     constructor call to get any appropriate diagnostics.  */
-	  init_expr = cp_build_indirect_ref (data_addr, RO_NULL, complain);
+	  init_expr = cp_build_indirect_ref (UNKNOWN_LOCATION, data_addr,
+					     RO_NULL, complain);
 	  if (type_build_ctor_call (elt_type))
 	    init_expr = build_special_member_call (init_expr,
 						   complete_ctor_identifier,
@@ -3101,7 +3108,8 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
 	}
       else
 	{
-	  init_expr = cp_build_indirect_ref (data_addr, RO_NULL, complain);
+	  init_expr = cp_build_indirect_ref (UNKNOWN_LOCATION, data_addr,
+					     RO_NULL, complain);
 
 	  if (type_build_ctor_call (type) && !explicit_value_init_p)
 	    {
@@ -3128,8 +3136,8 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
 
 	      ie = build_x_compound_expr_from_vec (*init, "new initializer",
 						   complain);
-	      init_expr = cp_build_modify_expr (init_expr, INIT_EXPR, ie,
-						complain);
+	      init_expr = cp_build_modify_expr (input_location, init_expr,
+						INIT_EXPR, ie, complain);
 	    }
 	  stable = stabilize_init (init_expr, &init_preeval_expr);
 	}
@@ -3266,7 +3274,7 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
    rather than just "new".  This may change PLACEMENT and INIT.  */
 
 tree
-build_new (vec<tree, va_gc> **placement, tree type, tree nelts,
+build_new (location_t loc, vec<tree, va_gc> **placement, tree type, tree nelts,
 	   vec<tree, va_gc> **init, int use_global_new, tsubst_flags_t complain)
 {
   tree rval;
@@ -3297,7 +3305,7 @@ build_new (vec<tree, va_gc> **placement, tree type, tree nelts,
 	  || (nelts && type_dependent_expression_p (nelts))
 	  || (nelts && *init)
 	  || any_type_dependent_arguments_p (*init))
-	return build_raw_new_expr (*placement, type, nelts, *init,
+	return build_raw_new_expr (loc, *placement, type, nelts, *init,
 				   use_global_new);
 
       orig_placement = make_tree_vector_copy (*placement);
@@ -3373,7 +3381,7 @@ build_new (vec<tree, va_gc> **placement, tree type, tree nelts,
 
   if (processing_template_decl)
     {
-      tree ret = build_raw_new_expr (orig_placement, type, orig_nelts,
+      tree ret = build_raw_new_expr (loc, orig_placement, type, orig_nelts,
 				     orig_init, use_global_new);
       release_tree_vector (orig_placement);
       release_tree_vector (orig_init);
@@ -3381,7 +3389,7 @@ build_new (vec<tree, va_gc> **placement, tree type, tree nelts,
     }
 
   /* Wrap it in a NOP_EXPR so warn_if_unused_value doesn't complain.  */
-  rval = build1 (NOP_EXPR, TREE_TYPE (rval), rval);
+  rval = build1_loc (loc, NOP_EXPR, TREE_TYPE (rval), rval);
   TREE_NO_WARNING (rval) = 1;
 
   return rval;
@@ -3516,7 +3524,7 @@ build_vec_delete_1 (tree base, tree maxindex, tree type,
 
   tbase = create_temporary_var (ptype);
   tbase_init
-    = cp_build_modify_expr (tbase, NOP_EXPR,
+    = cp_build_modify_expr (input_location, tbase, NOP_EXPR,
 			    fold_build_pointer_plus_loc (input_location,
 							 fold_convert (ptype,
 								       base),
@@ -3533,7 +3541,7 @@ build_vec_delete_1 (tree base, tree maxindex, tree type,
 			 fold_convert (ptype, base)));
   tmp = fold_build1_loc (input_location, NEGATE_EXPR, sizetype, size_exp);
   tmp = fold_build_pointer_plus (tbase, tmp);
-  tmp = cp_build_modify_expr (tbase, NOP_EXPR, tmp, complain);
+  tmp = cp_build_modify_expr (input_location, tbase, NOP_EXPR, tmp, complain);
   if (tmp == error_mark_node)
     return error_mark_node;
   body = build_compound_expr (input_location, body, tmp);
@@ -3651,8 +3659,8 @@ get_temp_regvar (tree type, tree init)
   decl = create_temporary_var (type);
   add_decl_expr (decl);
 
-  finish_expr_stmt (cp_build_modify_expr (decl, INIT_EXPR, init, 
-					  tf_warning_or_error));
+  finish_expr_stmt (cp_build_modify_expr (input_location, decl, INIT_EXPR,
+					  init, tf_warning_or_error));
 
   return decl;
 }
@@ -3916,7 +3924,7 @@ build_vec_init (tree base, tree maxindex, tree init,
 	  else if (MAYBE_CLASS_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
 	    one_init = build_aggr_init (baseref, elt, 0, complain);
 	  else
-	    one_init = cp_build_modify_expr (baseref, NOP_EXPR,
+	    one_init = cp_build_modify_expr (input_location, baseref, NOP_EXPR,
 					     elt, complain);
 	  if (one_init == error_mark_node)
 	    errors = true;
@@ -3948,14 +3956,15 @@ build_vec_init (tree base, tree maxindex, tree init,
 	    finish_expr_stmt (one_init);
 	  current_stmt_tree ()->stmts_are_full_exprs_p = 0;
 
-	  one_init = cp_build_unary_op (PREINCREMENT_EXPR, base, 0, complain);
+	  one_init = cp_build_unary_op (input_location, PREINCREMENT_EXPR,
+					base, 0, complain);
 	  if (one_init == error_mark_node)
 	    errors = true;
 	  else
 	    finish_expr_stmt (one_init);
 
-	  one_init = cp_build_unary_op (PREDECREMENT_EXPR, iterator, 0,
-					complain);
+	  one_init = cp_build_unary_op (input_location, PREDECREMENT_EXPR,
+					iterator, 0, complain);
 	  if (one_init == error_mark_node)
 	    errors = true;
 	  else
@@ -4007,8 +4016,8 @@ build_vec_init (tree base, tree maxindex, tree init,
       finish_for_cond (build2 (NE_EXPR, boolean_type_node, iterator,
 			       build_int_cst (TREE_TYPE (iterator), -1)),
 		       for_stmt, false);
-      elt_init = cp_build_unary_op (PREDECREMENT_EXPR, iterator, 0,
-				    complain);
+      elt_init = cp_build_unary_op (input_location, PREDECREMENT_EXPR,
+				    iterator, 0, complain);
       if (elt_init == error_mark_node)
 	errors = true;
       finish_for_expr (elt_init, for_stmt);
@@ -4044,13 +4053,13 @@ build_vec_init (tree base, tree maxindex, tree init,
 	    from = NULL_TREE;
 
 	  if (from_array == 2)
-	    elt_init = cp_build_modify_expr (to, NOP_EXPR, from, 
-					     complain);
+	    elt_init = cp_build_modify_expr (input_location, to, NOP_EXPR,
+					     from, complain);
 	  else if (type_build_ctor_call (type))
 	    elt_init = build_aggr_init (to, from, 0, complain);
 	  else if (from)
-	    elt_init = cp_build_modify_expr (to, NOP_EXPR, from,
-					     complain);
+	    elt_init = cp_build_modify_expr (input_location, to, NOP_EXPR,
+					     from, complain);
 	  else
 	    gcc_unreachable ();
 	}
@@ -4124,11 +4133,11 @@ build_vec_init (tree base, tree maxindex, tree init,
 	finish_expr_stmt (elt_init);
       current_stmt_tree ()->stmts_are_full_exprs_p = 0;
 
-      finish_expr_stmt (cp_build_unary_op (PREINCREMENT_EXPR, base, 0,
-                                           complain));
+      finish_expr_stmt (cp_build_unary_op (input_location, PREINCREMENT_EXPR,
+					   base, 0, complain));
       if (base2)
-	finish_expr_stmt (cp_build_unary_op (PREINCREMENT_EXPR, base2, 0,
-                                             complain));
+	finish_expr_stmt (cp_build_unary_op (input_location, PREINCREMENT_EXPR,
+					     base2, 0, complain));
 
       finish_for_stmt (for_stmt);
     }
@@ -4190,7 +4199,8 @@ build_vec_init (tree base, tree maxindex, tree init,
     {
       atype = build_pointer_type (atype);
       stmt_expr = build1 (NOP_EXPR, atype, stmt_expr);
-      stmt_expr = cp_build_indirect_ref (stmt_expr, RO_NULL, complain);
+      stmt_expr = cp_build_indirect_ref (UNKNOWN_LOCATION, stmt_expr,
+					 RO_NULL, complain);
       TREE_NO_WARNING (stmt_expr) = 1;
     }
 
@@ -4345,7 +4355,8 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
       /* Make sure the destructor is callable.  */
       if (type_build_dtor_call (type))
 	{
-	  expr = build_dtor_call (cp_build_indirect_ref (addr, RO_NULL,
+	  expr = build_dtor_call (cp_build_indirect_ref (UNKNOWN_LOCATION,
+							 addr, RO_NULL,
 							 complain),
 				  sfk_complete_destructor, flags, complain);
 	  if (expr == error_mark_node)
@@ -4422,7 +4433,8 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
 				complain);
 	}
 
-      expr = build_dtor_call (cp_build_indirect_ref (addr, RO_NULL, complain),
+      expr = build_dtor_call (cp_build_indirect_ref (UNKNOWN_LOCATION, addr,
+						     RO_NULL, complain),
 			      auto_delete, flags, complain);
       if (expr == error_mark_node)
 	return error_mark_node;
@@ -4591,7 +4603,8 @@ build_vec_delete (tree base, tree maxindex,
 				 sizetype, TYPE_SIZE_UNIT (sizetype));
       cookie_addr = fold_build_pointer_plus (fold_convert (size_ptr_type, base),
 					     cookie_addr);
-      maxindex = cp_build_indirect_ref (cookie_addr, RO_NULL, complain);
+      maxindex = cp_build_indirect_ref (input_location, cookie_addr, RO_NULL,
+					complain);
     }
   else if (TREE_CODE (type) == ARRAY_TYPE)
     {
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 533ae0e..cf9ac31 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -728,7 +728,7 @@ do_build_copy_assign (tree fndecl)
 	    init = move (init);
 
 	  if (DECL_NAME (field))
-	    init = cp_build_modify_expr (comp, NOP_EXPR, init, 
+	    init = cp_build_modify_expr (input_location, comp, NOP_EXPR, init,
 					 tf_warning_or_error);
 	  else
 	    init = build2 (MODIFY_EXPR, TREE_TYPE (comp), comp, init);
@@ -1014,7 +1014,7 @@ assignable_expr (tree to, tree from)
   ++cp_unevaluated_operand;
   to = build_stub_object (to);
   from = build_stub_object (from);
-  tree r = cp_build_modify_expr (to, NOP_EXPR, from, tf_none);
+  tree r = cp_build_modify_expr (input_location, to, NOP_EXPR, from, tf_none);
   --cp_unevaluated_operand;
   return r;
 }
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 0e1116b..88786b1 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -7643,9 +7643,9 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	      && !integer_zerop (cast_expression)
 	      && !TREE_OVERFLOW (cast_expression))
 	    {
-	      tree folded = fold_build1 (unary_operator,
-					 TREE_TYPE (cast_expression),
-					 cast_expression);
+	      tree folded = fold_build1_loc (loc, unary_operator,
+                                             TREE_TYPE (cast_expression),
+                                             cast_expression);
 	      if (CONSTANT_CLASS_P (folded) && !TREE_OVERFLOW (folded))
 		{
 		  expression = folded;
@@ -7811,8 +7811,8 @@ cp_parser_new_expression (cp_parser* parser)
   else
     {
       /* Create a representation of the new-expression.  */
-      ret = build_new (&placement, type, nelts, &initializer, global_scope_p,
-		       tf_warning_or_error);
+      ret = build_new (input_location, &placement, type, nelts, &initializer,
+                       global_scope_p, tf_warning_or_error);
     }
 
   if (placement != NULL)
@@ -8714,7 +8714,6 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 	  if (assignment_operator != ERROR_MARK)
 	    {
 	      bool non_constant_p;
-	      location_t saved_input_location;
 
 	      /* Parse the right-hand side of the assignment.  */
 	      tree rhs = cp_parser_initializer_clause (parser, &non_constant_p);
@@ -8729,13 +8728,10 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 		return error_mark_node;
 	      /* Build the assignment expression.  Its default
 		 location is the location of the '=' token.  */
-	      saved_input_location = input_location;
-	      input_location = loc;
 	      expr = build_x_modify_expr (loc, expr,
 					  assignment_operator,
 					  rhs,
 					  complain_flags (decltype_p));
-	      input_location = saved_input_location;
 	    }
 	}
     }
@@ -19245,7 +19241,8 @@ inject_this_parameter (tree ctype, cp_cv_quals quals)
   /* Clear this first to avoid shortcut in cp_build_indirect_ref.  */
   current_class_ptr = NULL_TREE;
   current_class_ref
-    = cp_build_indirect_ref (this_parm, RO_NULL, tf_warning_or_error);
+    = cp_build_indirect_ref (UNKNOWN_LOCATION, this_parm, RO_NULL,
+                             tf_warning_or_error);
   current_class_ptr = this_parm;
 }
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 2904657..4123e75 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15916,7 +15916,8 @@ tsubst_copy_and_build (tree t,
 				complain|decltype_flag));
 
     case FIX_TRUNC_EXPR:
-      RETURN (cp_build_unary_op (FIX_TRUNC_EXPR, RECUR (TREE_OPERAND (t, 0)),
+      RETURN (cp_build_unary_op (input_location, FIX_TRUNC_EXPR,
+                                 RECUR (TREE_OPERAND (t, 0)),
 				 0, complain));
 
     case ADDR_EXPR:
@@ -16171,7 +16172,7 @@ tsubst_copy_and_build (tree t,
 
 	tree op1 = tsubst (TREE_OPERAND (t, 1), args, complain, in_decl);
 	tree op2 = RECUR (TREE_OPERAND (t, 2));
-	ret = build_new (&placement_vec, op1, op2, &init_vec,
+	ret = build_new (UNKNOWN_LOCATION, &placement_vec, op1, op2, &init_vec,
 			 NEW_EXPR_USE_GLOBAL (t),
 			 complain);
 
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index b397b55..9e267cf 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -185,8 +185,8 @@ build_headof (tree exp)
   index = build_int_cst (NULL_TREE,
 			 -2 * TARGET_VTABLE_DATA_ENTRY_DISTANCE);
 
-  offset = build_vtbl_ref (cp_build_indirect_ref (exp, RO_NULL, 
-                                                  tf_warning_or_error), 
+  offset = build_vtbl_ref (cp_build_indirect_ref (UNKNOWN_LOCATION, exp,
+						  RO_NULL, tf_warning_or_error),
                            index);
 
   type = cp_build_qualified_type (ptr_type_node,
@@ -274,7 +274,7 @@ get_tinfo_decl_dynamic (tree exp, tsubst_flags_t complain)
     /* Otherwise return the type_info for the static type of the expr.  */
     t = get_tinfo_ptr (TYPE_MAIN_VARIANT (type));
 
-  return cp_build_indirect_ref (t, RO_NULL, complain);
+  return cp_build_indirect_ref (UNKNOWN_LOCATION, t, RO_NULL, complain);
 }
 
 static bool
@@ -334,7 +334,7 @@ build_typeid (tree exp, tsubst_flags_t complain)
       exp = cp_build_addr_expr (exp, complain);
       exp = stabilize_reference (exp);
       cond = cp_convert (boolean_type_node, exp, complain);
-      exp = cp_build_indirect_ref (exp, RO_NULL, complain);
+      exp = cp_build_indirect_ref (UNKNOWN_LOCATION, exp, RO_NULL, complain);
     }
 
   exp = get_tinfo_decl_dynamic (exp, complain);
@@ -498,7 +498,8 @@ get_typeid (tree type, tsubst_flags_t complain)
   if (!type)
     return error_mark_node;
 
-  return cp_build_indirect_ref (get_tinfo_ptr (type), RO_NULL, complain);
+  return cp_build_indirect_ref (UNKNOWN_LOCATION, get_tinfo_ptr (type),
+				RO_NULL, complain);
 }
 
 /* Check whether TEST is null before returning RESULT.  If TEST is used in
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index e7e5d8e..ead2bba 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -588,7 +588,8 @@ simplify_loop_decl_cond (tree *cond_p, tree body)
   *cond_p = boolean_true_node;
 
   if_stmt = begin_if_stmt ();
-  cond = cp_build_unary_op (TRUTH_NOT_EXPR, cond, 0, tf_warning_or_error);
+  cond = cp_build_unary_op (input_location, TRUTH_NOT_EXPR, cond, 0,
+			    tf_warning_or_error);
   finish_if_stmt_cond (cond, if_stmt);
   finish_break_stmt ();
   finish_then_clause (if_stmt);
@@ -2601,7 +2602,8 @@ finish_compound_literal (tree type, tree compound_literal,
       compound_literal
 	= finish_compound_literal (TREE_TYPE (type), compound_literal,
 				   complain);
-      return cp_build_c_cast (type, compound_literal, complain);
+      return cp_build_c_cast (UNKNOWN_LOCATION, type, compound_literal,
+			      complain);
     }
 
   if (!TYPE_OBJ_P (type))
@@ -7916,7 +7918,7 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv,
 	{
 	  if (orig_incr)
 	    TREE_VEC_ELT (orig_incr, i) = incr;
-	  incr = cp_build_modify_expr (TREE_OPERAND (incr, 0),
+	  incr = cp_build_modify_expr (input_location, TREE_OPERAND (incr, 0),
 				       TREE_CODE (TREE_OPERAND (incr, 1)),
 				       TREE_OPERAND (incr, 2),
 				       tf_warning_or_error);
@@ -7950,7 +7952,8 @@ finish_omp_for (location_t locus, enum tree_code code, tree declv,
       if (!processing_template_decl)
 	{
 	  init = fold_build_cleanup_point_expr (TREE_TYPE (init), init);
-	  init = cp_build_modify_expr (decl, NOP_EXPR, init, tf_warning_or_error);
+	  init = cp_build_modify_expr (input_location, decl, NOP_EXPR, init,
+				       tf_warning_or_error);
 	}
       else
 	init = build2 (MODIFY_EXPR, void_type_node, decl, init);
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index d2db31a..49ecf16 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -3186,7 +3186,8 @@ tree
 build_dummy_object (tree type)
 {
   tree decl = build1 (CONVERT_EXPR, build_pointer_type (type), void_node);
-  return cp_build_indirect_ref (decl, RO_NULL, tf_warning_or_error);
+  return cp_build_indirect_ref (UNKNOWN_LOCATION, decl, RO_NULL,
+				tf_warning_or_error);
 }
 
 /* We've gotten a reference to a member of TYPE.  Return *this if appropriate,
@@ -4099,7 +4100,8 @@ stabilize_expr (tree exp, tree* initp)
       exp = cp_build_addr_expr (exp, tf_warning_or_error);
       init_expr = get_target_expr (exp);
       exp = TARGET_EXPR_SLOT (init_expr);
-      exp = cp_build_indirect_ref (exp, RO_NULL, tf_warning_or_error);
+      exp = cp_build_indirect_ref (UNKNOWN_LOCATION, exp, RO_NULL,
+				   tf_warning_or_error);
       if (xval)
 	exp = move (exp);
     }
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 5f7d4bb..cc98813 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -37,7 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-ubsan.h"
 #include "params.h"
 
-static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
+static tree cp_build_addr_expr_strict (location_t, tree, tsubst_flags_t);
 static tree cp_build_function_call (tree, tree, tsubst_flags_t);
 static tree pfn_from_ptrmemfunc (tree);
 static tree delta_from_ptrmemfunc (tree);
@@ -2196,17 +2196,17 @@ rationalize_conditional_expr (enum tree_code code, tree t,
 						   op1, TREE_CODE (op1),
 						   /*overload=*/NULL,
 						   complain),
-                                cp_build_unary_op (code, op0, 0, complain),
-                                cp_build_unary_op (code, op1, 0, complain),
+                                cp_build_unary_op (loc, code, op0, 0, complain),
+                                cp_build_unary_op (loc, code, op1, 0, complain),
                                 complain);
     }
 
   return
     build_conditional_expr (loc, TREE_OPERAND (t, 0),
-			    cp_build_unary_op (code, TREE_OPERAND (t, 1), 0,
-                                               complain),
-			    cp_build_unary_op (code, TREE_OPERAND (t, 2), 0,
-                                               complain),
+			    cp_build_unary_op (loc, code, TREE_OPERAND (t, 1),
+					       0, complain),
+			    cp_build_unary_op (loc, code, TREE_OPERAND (t, 2),
+					       0, complain),
                             complain);
 }
 
@@ -2337,7 +2337,8 @@ build_class_member_access_expr (tree object, tree member,
   {
     tree temp = unary_complex_lvalue (ADDR_EXPR, object);
     if (temp)
-      object = cp_build_indirect_ref (temp, RO_NULL, complain);
+      object = cp_build_indirect_ref (UNKNOWN_LOCATION, temp, RO_NULL,
+				      complain);
   }
 
   /* In [expr.ref], there is an explicit list of the valid choices for
@@ -2907,7 +2908,7 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
   rval = build_new_op (loc, INDIRECT_REF, LOOKUP_NORMAL, expr,
 		       NULL_TREE, NULL_TREE, /*overload=*/NULL, complain);
   if (!rval)
-    rval = cp_build_indirect_ref (expr, errorstring, complain);
+    rval = cp_build_indirect_ref (loc, expr, errorstring, complain);
 
   if (processing_template_decl && rval != error_mark_node)
     return build_min_non_dep (INDIRECT_REF, rval, orig_expr);
@@ -2917,14 +2918,14 @@ build_x_indirect_ref (location_t loc, tree expr, ref_operator errorstring,
 
 /* Helper function called from c-common.  */
 tree
-build_indirect_ref (location_t /*loc*/,
+build_indirect_ref (location_t loc,
 		    tree ptr, ref_operator errorstring)
 {
-  return cp_build_indirect_ref (ptr, errorstring, tf_warning_or_error);
+  return cp_build_indirect_ref (loc, ptr, errorstring, tf_warning_or_error);
 }
 
 tree
-cp_build_indirect_ref (tree ptr, ref_operator errorstring, 
+cp_build_indirect_ref (location_t loc, tree ptr, ref_operator errorstring,
                        tsubst_flags_t complain)
 {
   tree pointer, type;
@@ -2979,7 +2980,7 @@ cp_build_indirect_ref (tree ptr, ref_operator errorstring,
 	return TREE_OPERAND (pointer, 0);
       else
 	{
-	  tree ref = build1 (INDIRECT_REF, t, pointer);
+	  tree ref = build1_loc (loc, INDIRECT_REF, t, pointer);
 
 	  /* We *must* set TREE_READONLY when dereferencing a pointer to const,
 	     so that we get the proper error message if the result is used
@@ -3203,12 +3204,12 @@ cp_build_array_ref (location_t loc, tree array, tree idx,
 
     warn_array_subscript_with_type_char (loc, idx);
 
-    ret = cp_build_indirect_ref (cp_build_binary_op (input_location,
+    ret = cp_build_indirect_ref (loc,
+				 cp_build_binary_op (input_location,
 						     PLUS_EXPR, ar, ind,
 						     complain),
                                  RO_ARRAY_INDEXING,
                                  complain);
-    protected_set_expr_location (ret, loc);
     if (non_lvalue)
       ret = non_lvalue_loc (loc, ret);
     return ret;
@@ -3358,13 +3359,13 @@ get_member_function_from_ptrfunc (tree *instance_ptrptr, tree function,
       /* Next extract the vtable pointer from the object.  */
       vtbl = build1 (NOP_EXPR, build_pointer_type (vtbl_ptr_type_node),
 		     instance_ptr);
-      vtbl = cp_build_indirect_ref (vtbl, RO_NULL, complain);
+      vtbl = cp_build_indirect_ref (input_location, vtbl, RO_NULL, complain);
       if (vtbl == error_mark_node)
 	return error_mark_node;
 
       /* Finally, extract the function pointer from the vtable.  */
       e2 = fold_build_pointer_plus_loc (input_location, vtbl, idx);
-      e2 = cp_build_indirect_ref (e2, RO_NULL, complain);
+      e2 = cp_build_indirect_ref (input_location, e2, RO_NULL, complain);
       if (e2 == error_mark_node)
 	return error_mark_node;
       TREE_CONSTANT (e2) = 1;
@@ -4955,8 +4956,10 @@ cp_build_binary_op (location_t location,
 	  if (first_complex)
 	    {
 	      op0 = save_expr (op0);
-	      real = cp_build_unary_op (REALPART_EXPR, op0, 1, complain);
-	      imag = cp_build_unary_op (IMAGPART_EXPR, op0, 1, complain);
+	      real = cp_build_unary_op (input_location, REALPART_EXPR, op0,
+					1, complain);
+	      imag = cp_build_unary_op (input_location, IMAGPART_EXPR, op0,
+					1, complain);
 	      switch (code)
 		{
 		case MULT_EXPR:
@@ -4975,8 +4978,10 @@ cp_build_binary_op (location_t location,
 	  else
 	    {
 	      op1 = save_expr (op1);
-	      real = cp_build_unary_op (REALPART_EXPR, op1, 1, complain);
-	      imag = cp_build_unary_op (IMAGPART_EXPR, op1, 1, complain);
+	      real = cp_build_unary_op (input_location, REALPART_EXPR, op1,
+					1, complain);
+	      imag = cp_build_unary_op (input_location, IMAGPART_EXPR, op1,
+					1, complain);
 	      switch (code)
 		{
 		case MULT_EXPR:
@@ -5355,7 +5360,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 	    }
 	}
 
-      exp = cp_build_addr_expr_strict (xarg, complain);
+      exp = cp_build_addr_expr_strict (loc, xarg, complain);
     }
 
   if (processing_template_decl && exp != error_mark_node)
@@ -5422,6 +5427,21 @@ build_address (tree t)
   return t;
 }
 
+/* As build_address, but at location LOC, rather than
+   UNKNOWN_LOCATION.  */
+
+tree
+build_address_loc (location_t loc, tree t)
+{
+  if (error_operand_p (t) || !cxx_mark_addressable (t))
+    return error_mark_node;
+  gcc_checking_assert (TREE_CODE (t) != CONSTRUCTOR);
+  t = build_fold_addr_expr_loc (loc, t);
+  if (TREE_CODE (t) != ADDR_EXPR)
+    t = rvalue (t);
+  return t;
+}
+
 /* Return a NOP_EXPR converting EXPR to TYPE.  */
 
 tree
@@ -5429,7 +5449,17 @@ build_nop (tree type, tree expr)
 {
   if (type == error_mark_node || error_operand_p (expr))
     return expr;
-  return build1 (NOP_EXPR, type, expr);
+  return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr);
+}
+
+/* Return a NOP_EXPR converting EXPR to TYPE.  */
+
+tree
+build_nop_loc (location_t loc, tree type, tree expr)
+{
+  if (type == error_mark_node || error_operand_p (expr))
+    return expr;
+  return build1_loc (loc, NOP_EXPR, type, expr);
 }
 
 /* Take the address of ARG, whatever that means under C++ semantics.
@@ -5440,7 +5470,8 @@ build_nop (tree type, tree expr)
    cp_build_addr_expr or cp_build_addr_expr_strict.  */
 
 static tree
-cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
+cp_build_addr_expr_1 (location_t loc, tree arg, bool strict_lvalue,
+		      tsubst_flags_t complain)
 {
   tree argtype;
   tree val;
@@ -5500,7 +5531,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
      address of a function is a no-op, so just return the
      argument.  */
   if (type_unknown_p (arg))
-    return build1 (ADDR_EXPR, unknown_type_node, arg);
+    return build1_loc (loc, ADDR_EXPR, unknown_type_node, arg);
 
   if (TREE_CODE (arg) == OFFSET_REF)
     /* We want a pointer to member; bypass all the code for actually taking
@@ -5534,7 +5565,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
   if (TREE_CODE (argtype) == REFERENCE_TYPE)
     {
       tree type = build_pointer_type (TREE_TYPE (argtype));
-      arg = build1 (CONVERT_EXPR, type, arg);
+      arg = build1_loc (loc, CONVERT_EXPR, type, arg);
       return arg;
     }
   else if (pedantic && DECL_MAIN_P (arg))
@@ -5561,7 +5592,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
       if (TREE_CODE (TREE_TYPE (arg)) == REFERENCE_TYPE)
 	{
 	  tree type = build_pointer_type (TREE_TYPE (TREE_TYPE (arg)));
-	  arg = build1 (CONVERT_EXPR, type, arg);
+	  arg = build1_loc (loc, CONVERT_EXPR, type, arg);
 	}
       else
 	/* Don't let this be an lvalue.  */
@@ -5579,7 +5610,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
       && TREE_CONSTANT (TREE_OPERAND (val, 0)))
     {
       tree type = build_pointer_type (argtype);
-      return fold_convert (type, fold_offsetof_1 (arg));
+      return fold_convert_loc (loc, type, fold_offsetof_1 (arg));
     }
 
   /* Handle complex lvalues (when permitted)
@@ -5648,7 +5679,7 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
      so we can just form an ADDR_EXPR with the correct type.  */
   if (processing_template_decl || TREE_CODE (arg) != COMPONENT_REF)
     {
-      val = build_address (arg);
+      val = build_address_loc (loc, arg);
       if (TREE_CODE (arg) == OFFSET_REF)
 	PTRMEM_OK_P (val) = PTRMEM_OK_P (arg);
     }
@@ -5701,15 +5732,15 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
 tree
 cp_build_addr_expr (tree arg, tsubst_flags_t complain)
 {
-  return cp_build_addr_expr_1 (arg, 0, complain);
+  return cp_build_addr_expr_1 (UNKNOWN_LOCATION, arg, 0, complain);
 }
 
 /* Take the address of ARG, but only if it's an lvalue.  */
 
 static tree
-cp_build_addr_expr_strict (tree arg, tsubst_flags_t complain)
+cp_build_addr_expr_strict (location_t loc, tree arg, tsubst_flags_t complain)
 {
-  return cp_build_addr_expr_1 (arg, 1, complain);
+  return cp_build_addr_expr_1 (loc, arg, 1, complain);
 }
 
 /* C++: Must handle pointers to members.
@@ -5722,8 +5753,8 @@ cp_build_addr_expr_strict (tree arg, tsubst_flags_t complain)
    (such as from short to int).  */
 
 tree
-cp_build_unary_op (enum tree_code code, tree xarg, int noconvert, 
-                   tsubst_flags_t complain)
+cp_build_unary_op (location_t loc, enum tree_code code, tree xarg,
+		   int noconvert, tsubst_flags_t complain)
 {
   /* No default_conversion here.  It causes trouble for ADDR_EXPR.  */
   tree arg = xarg;
@@ -5816,11 +5847,11 @@ cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
 
     case TRUTH_NOT_EXPR:
       if (VECTOR_TYPE_P (TREE_TYPE (arg)))
-	return cp_build_binary_op (input_location, EQ_EXPR, arg,
+	return cp_build_binary_op (loc, EQ_EXPR, arg,
 				   build_zero_cst (TREE_TYPE (arg)), complain);
       arg = perform_implicit_conversion (boolean_type_node, arg,
 					 complain);
-      val = invert_truthvalue_loc (input_location, arg);
+      val = invert_truthvalue_loc (loc, arg);
       if (arg != error_mark_node)
 	return val;
       errstring = _("in argument to unary !");
@@ -5854,13 +5885,15 @@ cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
 	  tree real, imag;
 
 	  arg = stabilize_reference (arg);
-	  real = cp_build_unary_op (REALPART_EXPR, arg, 1, complain);
-	  imag = cp_build_unary_op (IMAGPART_EXPR, arg, 1, complain);
-	  real = cp_build_unary_op (code, real, 1, complain);
+	  real = cp_build_unary_op (input_location, REALPART_EXPR, arg,
+				    1, complain);
+	  imag = cp_build_unary_op (input_location, IMAGPART_EXPR, arg,
+				    1, complain);
+	  real = cp_build_unary_op (input_location, code, real, 1, complain);
 	  if (real == error_mark_node || imag == error_mark_node)
 	    return error_mark_node;
-	  return build2 (COMPLEX_EXPR, TREE_TYPE (arg),
-			 real, imag);
+	  return build2_loc (loc, COMPLEX_EXPR, TREE_TYPE (arg),
+			     real, imag);
 	}
 
       /* Report invalid types.  */
@@ -5954,7 +5987,7 @@ cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
 	   need to ask Objective-C to build the increment or decrement
 	   expression for it.  */
 	if (objc_is_property_ref (arg))
-	  return objc_build_incr_expr_for_property_ref (input_location, code, 
+	  return objc_build_incr_expr_for_property_ref (loc, code,
 							arg, inc);	
 
 	/* Complain about anything else that is not a true lvalue.  */
@@ -5978,9 +6011,10 @@ cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
 	  }
 	else if (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR)
 	  /* An rvalue has no cv-qualifiers.  */
-	  val = build2 (code, cv_unqualified (TREE_TYPE (arg)), arg, inc);
+	  val = build2_loc (loc, code, cv_unqualified (TREE_TYPE (arg)), arg,
+			    inc);
 	else
-	  val = build2 (code, TREE_TYPE (arg), arg, inc);
+	  val = build2_loc (loc, code, TREE_TYPE (arg), arg, inc);
 
 	TREE_SIDE_EFFECTS (val) = 1;
 	return val;
@@ -5999,7 +6033,7 @@ cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
     {
       if (argtype == 0)
 	argtype = TREE_TYPE (arg);
-      return build1 (code, argtype, arg);
+      return build1_loc (loc, code, argtype, arg);
     }
 
   if (complain & tf_error)
@@ -6009,10 +6043,11 @@ cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
 
 /* Hook for the c-common bits that build a unary op.  */
 tree
-build_unary_op (location_t /*location*/,
+build_unary_op (location_t location,
 		enum tree_code code, tree xarg, int noconvert)
 {
-  return cp_build_unary_op (code, xarg, noconvert, tf_warning_or_error);
+  return cp_build_unary_op (location, code, xarg, noconvert,
+			    tf_warning_or_error);
 }
 
 /* Apply unary lvalue-demanding operator CODE to the expression ARG
@@ -6034,7 +6069,8 @@ unary_complex_lvalue (enum tree_code code, tree arg)
   /* Handle (a, b) used as an "lvalue".  */
   if (TREE_CODE (arg) == COMPOUND_EXPR)
     {
-      tree real_result = cp_build_unary_op (code, TREE_OPERAND (arg, 1), 0,
+      tree real_result = cp_build_unary_op (input_location, code,
+					    TREE_OPERAND (arg, 1), 0,
                                             tf_warning_or_error);
       return build2 (COMPOUND_EXPR, TREE_TYPE (real_result),
 		     TREE_OPERAND (arg, 0), real_result);
@@ -6068,7 +6104,8 @@ unary_complex_lvalue (enum tree_code code, tree arg)
   if (TREE_CODE (arg) == MODIFY_EXPR
       || TREE_CODE (arg) == INIT_EXPR)
     {
-      tree real_result = cp_build_unary_op (code, TREE_OPERAND (arg, 0), 0,
+      tree real_result = cp_build_unary_op (input_location, code,
+					    TREE_OPERAND (arg, 0), 0,
                                             tf_warning_or_error);
       arg = build2 (COMPOUND_EXPR, TREE_TYPE (real_result),
 		    arg, real_result);
@@ -6519,7 +6556,7 @@ convert_ptrmem (tree type, tree expr, bool allow_inverse_p,
    indicate whether or not the cast was valid.  */
 
 static tree
-build_static_cast_1 (tree type, tree expr, bool c_cast_p,
+build_static_cast_1 (location_t loc, tree type, tree expr, bool c_cast_p,
 		     bool *valid_p, tsubst_flags_t complain)
 {
   tree intype;
@@ -6623,7 +6660,7 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
 	  /* Make sure we don't fold back down to a named rvalue reference,
 	     because that would be an lvalue.  */
 	  if (DECL_P (result))
-	    result = build1 (NON_LVALUE_EXPR, type, result);
+	    result = build1_loc (loc, NON_LVALUE_EXPR, type, result);
 	  return convert_from_reference (result);
 	}
       else
@@ -6673,7 +6710,7 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
 
 	  if (result == expr && SCALAR_TYPE_P (type))
 	    /* Leave some record of the cast.  */
-	    result = build_nop (type, expr);
+	    result = build_nop_loc (loc, type, expr);
 	}
       return result;
     }
@@ -6787,7 +6824,7 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
 	  && check_for_casting_away_constness (intype, type, STATIC_CAST_EXPR,
 					       complain))
 	return error_mark_node;
-      return build_nop (type, expr);
+      return build_nop_loc (loc, type, expr);
     }
 
   *valid_p = false;
@@ -6820,8 +6857,8 @@ build_static_cast (tree type, tree expr, tsubst_flags_t complain)
       && TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
     expr = TREE_OPERAND (expr, 0);
 
-  result = build_static_cast_1 (type, expr, /*c_cast_p=*/false, &valid_p,
-                                complain);
+  result = build_static_cast_1 (input_location, type, expr, /*c_cast_p=*/false,
+				&valid_p, complain);
   if (valid_p)
     {
       if (result != error_mark_node)
@@ -6881,7 +6918,7 @@ convert_member_func_to_ptr (tree type, tree expr, tsubst_flags_t complain)
    indicate whether or not reinterpret_cast was valid.  */
 
 static tree
-build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
+build_reinterpret_cast_1 (location_t loc, tree type, tree expr, bool c_cast_p,
 			  bool *valid_p, tsubst_flags_t complain)
 {
   tree intype;
@@ -6931,7 +6968,8 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
 
       if (expr != error_mark_node)
 	expr = build_reinterpret_cast_1
-	  (build_pointer_type (TREE_TYPE (type)), expr, c_cast_p,
+	  (input_location,
+	   build_pointer_type (TREE_TYPE (type)), expr, c_cast_p,
 	   valid_p, complain);
       if (expr != error_mark_node)
 	/* cp_build_indirect_ref isn't right for rvalue refs.  */
@@ -7027,7 +7065,7 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
       if (warn_strict_aliasing <= 2)
 	strict_aliasing_warning (intype, type, sexpr);
 
-      return build_nop (type, expr);
+      return build_nop_loc (loc, type, expr);
     }
   else if ((TYPE_PTRFN_P (type) && TYPE_PTROBV_P (intype))
 	   || (TYPE_PTRFN_P (intype) && TYPE_PTROBV_P (type)))
@@ -7038,7 +7076,7 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
 	warning (OPT_Wconditionally_supported,
 		 "casting between pointer-to-function and pointer-to-object "
 		 "is conditionally-supported");
-      return build_nop (type, expr);
+      return build_nop_loc (loc, type, expr);
     }
   else if (VECTOR_TYPE_P (type))
     return convert_to_vector (type, expr);
@@ -7076,7 +7114,8 @@ build_reinterpret_cast (tree type, tree expr, tsubst_flags_t complain)
       return convert_from_reference (t);
     }
 
-  r = build_reinterpret_cast_1 (type, expr, /*c_cast_p=*/false,
+  r = build_reinterpret_cast_1 (input_location,
+				type, expr, /*c_cast_p=*/false,
 				/*valid_p=*/NULL, complain);
   if (r != error_mark_node)
     maybe_warn_about_useless_cast (type, expr, complain);
@@ -7091,8 +7130,8 @@ build_reinterpret_cast (tree type, tree expr, tsubst_flags_t complain)
    whether or not the conversion succeeded.  */
 
 static tree
-build_const_cast_1 (tree dst_type, tree expr, tsubst_flags_t complain,
-		    bool *valid_p)
+build_const_cast_1 (location_t loc, tree dst_type, tree expr,
+		    tsubst_flags_t complain, bool *valid_p)
 {
   tree src_type;
   tree reference_type;
@@ -7195,7 +7234,7 @@ build_const_cast_1 (tree dst_type, tree expr, tsubst_flags_t complain,
 	      expr = cp_build_addr_expr (expr, complain);
 	      if (expr == error_mark_node)
 		return error_mark_node;
-	      expr = build_nop (reference_type, expr);
+	      expr = build_nop_loc (loc, reference_type, expr);
 	      return convert_from_reference (expr);
 	    }
 	  else
@@ -7210,7 +7249,7 @@ build_const_cast_1 (tree dst_type, tree expr, tsubst_flags_t complain,
 	      if (TREE_CODE (expr) == NOP_EXPR
 		  && TREE_TYPE (expr) == TREE_TYPE (TREE_OPERAND (expr, 0)))
 		expr = TREE_OPERAND (expr, 0);
-	      return build_nop (dst_type, expr);
+	      return build_nop_loc (loc, dst_type, expr);
 	    }
 	}
       else if (valid_p
@@ -7245,7 +7284,8 @@ build_const_cast (tree type, tree expr, tsubst_flags_t complain)
       return convert_from_reference (t);
     }
 
-  r = build_const_cast_1 (type, expr, complain, /*valid_p=*/NULL);
+  r = build_const_cast_1 (input_location, type, expr, complain,
+			  /*valid_p=*/NULL);
   if (r != error_mark_node)
     maybe_warn_about_useless_cast (type, expr, complain);
   return r;
@@ -7254,16 +7294,16 @@ build_const_cast (tree type, tree expr, tsubst_flags_t complain)
 /* Like cp_build_c_cast, but for the c-common bits.  */
 
 tree
-build_c_cast (location_t /*loc*/, tree type, tree expr)
+build_c_cast (location_t loc, tree type, tree expr)
 {
-  return cp_build_c_cast (type, expr, tf_warning_or_error);
+  return cp_build_c_cast (loc, type, expr, tf_warning_or_error);
 }
 
 /* Build an expression representing an explicit C-style cast to type
    TYPE of expression EXPR.  */
 
 tree
-cp_build_c_cast (tree type, tree expr, tsubst_flags_t complain)
+cp_build_c_cast (location_t loc, tree type, tree expr, tsubst_flags_t complain)
 {
   tree value = expr;
   tree result;
@@ -7286,7 +7326,7 @@ cp_build_c_cast (tree type, tree expr, tsubst_flags_t complain)
      in method lookup.  */
   if (objc_is_object_ptr (type)
       && objc_is_object_ptr (TREE_TYPE (expr)))
-    return build_nop (type, expr);
+    return build_nop_loc (loc, type, expr);
 
   /* build_c_cast puts on a NOP_EXPR to make the result not an lvalue.
      Strip such NOP_EXPRs if VALUE is being used in non-lvalue context.  */
@@ -7333,7 +7373,7 @@ cp_build_c_cast (tree type, tree expr, tsubst_flags_t complain)
 		"cast to pointer from integer of different size");
 
   /* A C-style cast can be a const_cast.  */
-  result = build_const_cast_1 (type, value, complain & tf_warning,
+  result = build_const_cast_1 (loc, type, value, complain & tf_warning,
 			       &valid_p);
   if (valid_p)
     {
@@ -7343,11 +7383,11 @@ cp_build_c_cast (tree type, tree expr, tsubst_flags_t complain)
     }
 
   /* Or a static cast.  */
-  result = build_static_cast_1 (type, value, /*c_cast_p=*/true,
+  result = build_static_cast_1 (loc, type, value, /*c_cast_p=*/true,
 				&valid_p, complain);
   /* Or a reinterpret_cast.  */
   if (!valid_p)
-    result = build_reinterpret_cast_1 (type, value, /*c_cast_p=*/true,
+    result = build_reinterpret_cast_1 (loc, type, value, /*c_cast_p=*/true,
 				       &valid_p, complain);
   /* The static_cast or reinterpret_cast may be followed by a
      const_cast.  */
@@ -7373,7 +7413,7 @@ cp_build_c_cast (tree type, tree expr, tsubst_flags_t complain)
 	 to succeed.  */
       if (!same_type_p (non_reference (type), non_reference (result_type)))
 	{
-	  result = build_const_cast_1 (type, result, false, &valid_p);
+	  result = build_const_cast_1 (loc, type, result, false, &valid_p);
 	  gcc_assert (valid_p);
 	}
       return result;
@@ -7384,13 +7424,14 @@ cp_build_c_cast (tree type, tree expr, tsubst_flags_t complain)
 \f
 /* For use from the C common bits.  */
 tree
-build_modify_expr (location_t /*location*/,
+build_modify_expr (location_t location,
 		   tree lhs, tree /*lhs_origtype*/,
 		   enum tree_code modifycode, 
 		   location_t /*rhs_location*/, tree rhs,
 		   tree /*rhs_origtype*/)
 {
-  return cp_build_modify_expr (lhs, modifycode, rhs, tf_warning_or_error);
+  return cp_build_modify_expr (location, lhs, modifycode, rhs,
+			       tf_warning_or_error);
 }
 
 /* Build an assignment expression of lvalue LHS from value RHS.
@@ -7401,8 +7442,8 @@ build_modify_expr (location_t /*location*/,
    C++: If MODIFYCODE is INIT_EXPR, then leave references unbashed.  */
 
 tree
-cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
-		      tsubst_flags_t complain)
+cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
+		      tree rhs, tsubst_flags_t complain)
 {
   tree result;
   tree newrhs = rhs;
@@ -7424,31 +7465,31 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
 	lhs = build2 (TREE_CODE (lhs), TREE_TYPE (lhs),
 		      stabilize_reference (TREE_OPERAND (lhs, 0)),
 		      TREE_OPERAND (lhs, 1));
-      newrhs = cp_build_modify_expr (TREE_OPERAND (lhs, 0),
+      newrhs = cp_build_modify_expr (loc, TREE_OPERAND (lhs, 0),
 				     modifycode, rhs, complain);
       if (newrhs == error_mark_node)
 	return error_mark_node;
-      return build2 (COMPOUND_EXPR, lhstype, lhs, newrhs);
+      return build2_loc (loc, COMPOUND_EXPR, lhstype, lhs, newrhs);
 
       /* Handle (a, b) used as an "lvalue".  */
     case COMPOUND_EXPR:
-      newrhs = cp_build_modify_expr (TREE_OPERAND (lhs, 1),
+      newrhs = cp_build_modify_expr (loc, TREE_OPERAND (lhs, 1),
 				     modifycode, rhs, complain);
       if (newrhs == error_mark_node)
 	return error_mark_node;
-      return build2 (COMPOUND_EXPR, lhstype,
-		     TREE_OPERAND (lhs, 0), newrhs);
+      return build2_loc (loc, COMPOUND_EXPR, lhstype,
+			 TREE_OPERAND (lhs, 0), newrhs);
 
     case MODIFY_EXPR:
       if (TREE_SIDE_EFFECTS (TREE_OPERAND (lhs, 0)))
 	lhs = build2 (TREE_CODE (lhs), TREE_TYPE (lhs),
 		      stabilize_reference (TREE_OPERAND (lhs, 0)),
 		      TREE_OPERAND (lhs, 1));
-      newrhs = cp_build_modify_expr (TREE_OPERAND (lhs, 0), modifycode, rhs,
-				     complain);
+      newrhs = cp_build_modify_expr (loc, TREE_OPERAND (lhs, 0), modifycode,
+				     rhs, complain);
       if (newrhs == error_mark_node)
 	return error_mark_node;
-      return build2 (COMPOUND_EXPR, lhstype, lhs, newrhs);
+      return build2_loc (loc, COMPOUND_EXPR, lhstype, lhs, newrhs);
 
     case MIN_EXPR:
     case MAX_EXPR:
@@ -7493,10 +7534,10 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
 	  return error_mark_node;
 
 	cond = build_conditional_expr
-	  (input_location, TREE_OPERAND (lhs, 0),
-	   cp_build_modify_expr (TREE_OPERAND (lhs, 1),
+	  (loc, TREE_OPERAND (lhs, 0),
+	   cp_build_modify_expr (loc, TREE_OPERAND (lhs, 1),
 				 modifycode, rhs, complain),
-	   cp_build_modify_expr (TREE_OPERAND (lhs, 2),
+	   cp_build_modify_expr (loc, TREE_OPERAND (lhs, 2),
 				 modifycode, rhs, complain),
            complain);
 
@@ -7505,7 +7546,8 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
 	/* Make sure the code to compute the rhs comes out
 	   before the split.  */
 	if (preeval)
-	  cond = build2 (COMPOUND_EXPR, TREE_TYPE (lhs), preeval, cond);
+	  cond = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (lhs), preeval,
+			     cond);
 	return cond;
       }
 
@@ -7523,7 +7565,7 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
 	  if (! same_type_p (TREE_TYPE (rhs), lhstype))
 	    /* Call convert to generate an error; see PR 11063.  */
 	    rhs = convert (lhstype, rhs);
-	  result = build2 (INIT_EXPR, lhstype, lhs, rhs);
+	  result = build2_loc (loc, INIT_EXPR, lhstype, lhs, rhs);
 	  TREE_SIDE_EFFECTS (result) = 1;
 	  return result;
 	}
@@ -7561,7 +7603,7 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
 	    /* Do the default thing.  */;
 	  else
 	    {
-	      result = build_new_op (input_location, MODIFY_EXPR,
+	      result = build_new_op (loc, MODIFY_EXPR,
 				     LOOKUP_NORMAL, lhs, rhs,
 				     make_node (NOP_EXPR), /*overload=*/NULL,
 				     complain);
@@ -7594,7 +7636,7 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
 	  lhs = stabilize_reference (lhs);
 	  rhs = rvalue (rhs);
 	  rhs = stabilize_expr (rhs, &init);
-	  newrhs = cp_build_binary_op (input_location,
+	  newrhs = cp_build_binary_op (loc,
 				       modifycode, lhs, rhs,
 				       complain);
 	  if (newrhs == error_mark_node)
@@ -7606,7 +7648,8 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
 	    }
 
 	  if (init)
-	    newrhs = build2 (COMPOUND_EXPR, TREE_TYPE (newrhs), init, newrhs);
+	    newrhs = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (newrhs), init,
+				 newrhs);
 
 	  /* Now it looks like a plain assignment.  */
 	  modifycode = NOP_EXPR;
@@ -7754,8 +7797,9 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
 	return result;
     }
 
-  result = build2 (modifycode == NOP_EXPR ? MODIFY_EXPR : INIT_EXPR,
-		   lhstype, lhs, newrhs);
+  result = build2_loc (loc,
+		       modifycode == NOP_EXPR ? MODIFY_EXPR : INIT_EXPR,
+		       lhstype, lhs, newrhs);
 
   TREE_SIDE_EFFECTS (result) = 1;
   if (!plain_assign)
@@ -7784,7 +7828,7 @@ build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 	  return rval;
 	}
     }
-  return cp_build_modify_expr (lhs, modifycode, rhs, complain);
+  return cp_build_modify_expr (loc, lhs, modifycode, rhs, complain);
 }
 
 /* Helper function for get_delta_difference which assumes FROM is a base
@@ -8024,7 +8068,7 @@ build_ptrmemfunc (tree type, tree pfn, int force, bool c_cast_p,
   /* Handle null pointer to member function conversions.  */
   if (null_ptr_cst_p (pfn))
     {
-      pfn = cp_build_c_cast (type, pfn, complain);
+      pfn = cp_build_c_cast (UNKNOWN_LOCATION, type, pfn, complain);
       return build_ptrmemfunc1 (to_type,
 				integer_zero_node,
 				pfn);
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 839091c..51fbc77 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1740,7 +1740,7 @@ build_x_arrow (location_t loc, tree expr, tsubst_flags_t complain)
 	  return expr;
 	}
 
-      return cp_build_indirect_ref (last_rval, RO_NULL, complain);
+      return cp_build_indirect_ref (loc, last_rval, RO_NULL, complain);
     }
 
   if (complain & tf_error)
@@ -1841,7 +1841,8 @@ build_m_component_ref (tree datum, tree component, tsubst_flags_t complain)
 	 value stored in the pointer-to-data-member.  */
       ptype = build_pointer_type (type);
       datum = fold_build_pointer_plus (fold_convert (ptype, datum), component);
-      datum = cp_build_indirect_ref (datum, RO_NULL, complain);
+      datum = cp_build_indirect_ref (UNKNOWN_LOCATION, datum, RO_NULL,
+				     complain);
       if (datum == error_mark_node)
 	return error_mark_node;
 
@@ -1955,7 +1956,7 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
 
       /* This must build a C cast.  */
       parms = build_x_compound_expr_from_list (parms, ELK_FUNC_CAST, complain);
-      return cp_build_c_cast (type, parms, complain);
+      return cp_build_c_cast (UNKNOWN_LOCATION, type, parms, complain);
     }
 
   /* Prepare to evaluate as a call to a constructor.  If this expression
@@ -1976,7 +1977,8 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
      conversion is equivalent (in definedness, and if defined in
      meaning) to the corresponding cast expression.  */
   if (parms && TREE_CHAIN (parms) == NULL_TREE)
-    return cp_build_c_cast (type, TREE_VALUE (parms), complain);
+    return cp_build_c_cast (UNKNOWN_LOCATION, type, TREE_VALUE (parms),
+			    complain);
 
   /* [expr.type.conv]
 
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 2/2] RFC: C++ FE: expression ranges (work in progress) v3
  2015-11-25 20:32       ` [PATCH/RFC 0/2] C++ FE: expression ranges (v3) David Malcolm
  2015-11-25 20:32         ` [PATCH 1/2] RFC: C++: attempt to provide location_t in more places David Malcolm
@ 2015-11-25 20:38         ` David Malcolm
  1 sibling, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-11-25 20:38 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

Changes from previous version:

- all new calls to protected_set_expr_location removed; instead
  the location is passed into a tree-building function, using
  the preceding patch.

- removal of #if 0 code

- generates meaningful ranges for new expressions, using
  cp_lexer_previous_token.

- shared the range-testing-plugin directly with the C FE, rather
  than having an identical copy; renamed the testcase from .c to .C

Bootstraps (x86_64-pc-linux-gnu) but regresses the following test
cases, due to changes in diagnostic location:
  g++.dg/gomp/loop-1.C
  g++.dg/gomp/loop-2.C
  g++.dg/gomp/loop-3.C
  g++.dg/init/const7.C
  g++.dg/ubsan/pr63956.C
  g++.dg/warn/Wparentheses-26.C

gcc/cp/ChangeLog:
	* cp-tree.h (class cp_expr): New class.
	(perform_koenig_lookup): Convert return type and param from tree
	to cp_expr.
	(finish_increment_expr): Likewise.
	(finish_unary_op_expr): Likewise.
	(finish_id_expression): Likewise for return type.
	(build_class_member_access_expr): Likewise for param.
	(finish_class_member_access_expr): Likewise.
	(build_x_unary_op): Likewise.
	(build_c_cast): New decl.
	(build_x_modify_expr): Convert return type from tree to cp_expr.
	* name-lookup.c (lookup_arg_dependent_1): Likewise.
	(lookup_arg_dependent): Likewise; also for local "ret".
	* name-lookup.h (lookup_arg_dependent): Likewise for return type.
	* parser.c (struct cp_parser_expression_stack_entry): Likewise
	for field "lhs".
	(cp_parser_identifier): Likewise for return type.  Use cp_expr
	ctor to preserve the token's location.
	(cp_parser_string_literal): Likewise, building up a meaningful
	location for the case where a compound string literal is built by
	concatentation.
	(cp_parser_userdef_char_literal): Likewise for return type.
	(cp_parser_userdef_numeric_literal): Likewise.
	(cp_parser_fold_expression): Likewise.
	(cp_parser_primary_expression): Likewise, and for locals "expr",
	"lam", "id_expression", "decl".
	Use cp_expr ctor when parsing literals, to preserve the spelling
	location of the token.  Preserve the locations of parentheses.
	Preserve location when calling objc_lookup_ivar.
	(cp_parser_primary_expression): Convert return type from tree to
	cp_expr.
	(cp_parser_id_expression): Likewise.
	(cp_parser_unqualified_id): Likewise.  Also for local "id".
	(cp_parser_postfix_expression): Likewise, also for local
	"postfix_expression".  Preserve start location.  Use it
	to construct spelling ranges for C++-style casts.  Pass on the
	location of the closing parenthesis of a call site to
	cp_parser_parenthesized_expression_list, and use it to build
	a source range for a call.  Use cp_expr in ternary expression.
	(cp_parser_postfix_dot_deref_expression): Convert param from tree to
	cp_expr.
	(cp_parser_parenthesized_expression_list): Add "close_paren_loc"
	out-param, and write back to it.
	(cp_parser_unary_expression): Convert return type from tree to
	cp_expr.  Also for locals "cast_expression" and "expression".
	Generate and use suitable locations for cast expressions.
	(cp_parser_new_expression): Generate meaningful locations/ranges.
	(cp_parser_cast_expression): Convert return type from tree to
	cp_expr; also for local "expr".  Use the paren location to generate a
	meaningful range for the expression.
	(cp_parser_binary_expression): Convert return type from tree to
	cp_expr; also for local "rhs".  Generate a meaningful location
	for the expression, and use it.  Replace call to
	protected_set_expr_location by converting a build2 to a
	build2_loc and using the location in the call to
	build_x_binary_op.
	(cp_parser_question_colon_clause): Convert param from tree to
	cp_expr; also for local "assignment_expr".  Set the spelling range
	of the expression.
	(cp_parser_assignment_expression): Likewise for return type and
	locals "expr" and "rhs".  Build a meaningful spelling range for
	the expression.
	(cp_parser_expression): Likewise for return type and locals
	"expression" and "assignment_expression".  Build a meaningful
	spelling range for assignment expressions.
	(cp_parser_constant_expression): Likewise for return type and
	local "expression".
	(cp_parser_lambda_expression): Likewise for return type.
	(cp_parser_operator_function_id): Likewise.
	(cp_parser_operator): Likewise.  Generate a meaningful range,
	using cp_expr's ctor to return it.
	(cp_parser_initializer_clause): Likewise for local "initializer".
	(cp_parser_lookup_name): Likewise for return type.  Use cp_expr's
	ctor to preserve the location_t of the name.
	(cp_parser_simple_cast_expression): Likewise for return type.
	* semantics.c (perform_koenig_lookup): Likewise for return type
	and param.
	(finish_increment_expr): Likewise.  Generate a meaningful spelling
	range.
	(finish_unary_op_expr): Likewise.
	(finish_id_expression): Likewise.
	* typeck.c (build_class_member_access_expr): Likewise for param;
	generate range for result.
	(finish_class_member_access_expr): Likewise.
	(cp_build_binary_op): Convert a build2 to a build2_loc.
	(build_x_unary_op): Convert param from tree to cp_expr.
	(build_c_cast): Provide an overloaded variant that takes a cp_expr
	and returns a cp_expr.
	(build_x_modify_expr): Convert return type from tree to cp_expr.

gcc/testsuite/ChangeLog:
	* g++.dg/plugin/diagnostic-test-expressions-1.C: New file.
	* g++.dg/plugin/plugin.exp (plugin_test_list): Add the above.

gcc/ChangeLog:
	* tree.c (get_pure_location): Make non-static.
	(set_source_range): Return the resulting location_t.
	(make_location): New function.
	* tree.h (get_pure_location): New decl.
	(set_source_range): Convert return type from void to location_t.
	(make_location): New decl.
---
 gcc/cp/cp-tree.h                                   |  84 +++-
 gcc/cp/name-lookup.c                               |   6 +-
 gcc/cp/name-lookup.h                               |   2 +-
 gcc/cp/parser.c                                    | 332 +++++++++----
 gcc/cp/semantics.c                                 |  35 +-
 gcc/cp/typeck.c                                    |  46 +-
 .../g++.dg/plugin/diagnostic-test-expressions-1.C  | 532 +++++++++++++++++++++
 gcc/testsuite/g++.dg/plugin/plugin.exp             |   5 +-
 gcc/tree.c                                         |  25 +-
 gcc/tree.h                                         |   8 +-
 10 files changed, 923 insertions(+), 152 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4878161..be379f8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -40,6 +40,72 @@ c-common.h, not after.
 #include "c-family/c-common.h"
 #include "diagnostic.h"
 
+/* A tree node, together with a location, so that we can track locations
+   (and ranges) during parsing.
+
+   The location is redundant for node kinds that have locations,
+   but not all node kinds do (e.g. constants, and references to
+   params, locals, etc), so we stash a copy here.  */
+
+class cp_expr
+{
+public:
+  cp_expr () :
+    m_value (NULL), m_loc (UNKNOWN_LOCATION) {}
+
+  cp_expr (tree value) :
+    m_value (value), m_loc (EXPR_LOCATION (m_value)) {}
+
+  cp_expr (tree value, location_t loc):
+    m_value (value), m_loc (loc)
+  {
+    if (m_value)
+      gcc_assert (m_loc != UNKNOWN_LOCATION);
+  }
+
+  cp_expr (const cp_expr &other) :
+    m_value (other.m_value), m_loc (other.m_loc) {}
+
+  /* Implicit conversions to tree.  */
+  operator tree () const { return m_value; }
+  tree & operator* () { return m_value; }
+  tree & operator-> () { return m_value; }
+
+  tree get_value () const { return m_value; }
+  location_t get_location () const { return m_loc; }
+  location_t get_start () const
+  {
+    source_range src_range = get_range_from_loc (line_table, m_loc);
+    return src_range.m_start;
+  }
+  location_t get_finish () const
+  {
+    source_range src_range = get_range_from_loc (line_table, m_loc);
+    return src_range.m_finish;
+  }
+
+  void set_location (location_t loc)
+  {
+    protected_set_expr_location (m_value, loc);
+    m_loc = loc;
+  }
+
+  void set_range (location_t start, location_t finish)
+  {
+    m_loc = set_source_range (m_value, start, finish);
+  }
+
+ private:
+  tree m_value;
+  location_t m_loc;
+};
+
+inline bool
+operator == (const cp_expr &lhs, tree rhs)
+{
+  return lhs.get_value () == rhs;
+}
+
 #include "name-lookup.h"
 
 /* Usage of TREE_LANG_FLAG_?:
@@ -6289,15 +6355,15 @@ extern tree finish_stmt_expr_expr		(tree, tree);
 extern tree finish_stmt_expr			(tree, bool);
 extern tree stmt_expr_value_expr		(tree);
 bool empty_expr_stmt_p				(tree);
-extern tree perform_koenig_lookup		(tree, vec<tree, va_gc> *,
+extern cp_expr perform_koenig_lookup		(cp_expr, vec<tree, va_gc> *,
 						 tsubst_flags_t);
 extern tree finish_call_expr			(tree, vec<tree, va_gc> **, bool,
 						 bool, tsubst_flags_t);
 extern tree finish_template_variable		(tree, tsubst_flags_t = tf_warning_or_error);
-extern tree finish_increment_expr		(tree, enum tree_code);
+extern cp_expr finish_increment_expr		(cp_expr, enum tree_code);
 extern tree finish_this_expr			(void);
 extern tree finish_pseudo_destructor_expr       (tree, tree, tree, location_t);
-extern tree finish_unary_op_expr		(location_t, enum tree_code, tree,
+extern cp_expr finish_unary_op_expr		(location_t, enum tree_code, cp_expr,
 						 tsubst_flags_t);
 extern tree finish_compound_literal		(tree, tree, tsubst_flags_t);
 extern tree finish_fname			(tree);
@@ -6311,7 +6377,7 @@ extern tree finish_base_specifier		(tree, tree, bool);
 extern void finish_member_declaration		(tree);
 extern bool outer_automatic_var_p		(tree);
 extern tree process_outer_var_ref		(tree, tsubst_flags_t);
-extern tree finish_id_expression		(tree, tree, tree,
+extern cp_expr finish_id_expression		(tree, tree, tree,
 						 cp_id_kind *,
 						 bool, bool, bool *,
 						 bool, bool, bool, bool,
@@ -6553,9 +6619,9 @@ extern tree unlowered_expr_type                 (const_tree);
 extern tree decay_conversion			(tree,
                                                  tsubst_flags_t,
                                                  bool = true);
-extern tree build_class_member_access_expr      (tree, tree, tree, bool,
+extern tree build_class_member_access_expr      (cp_expr, tree, tree, bool,
 						 tsubst_flags_t);
-extern tree finish_class_member_access_expr     (tree, tree, bool, 
+extern tree finish_class_member_access_expr     (cp_expr, tree, bool,
 						 tsubst_flags_t);
 extern tree build_x_indirect_ref		(location_t, tree,
 						 ref_operator, tsubst_flags_t);
@@ -6577,7 +6643,7 @@ extern tree build_x_binary_op			(location_t,
 extern tree build_x_array_ref			(location_t, tree, tree,
 						 tsubst_flags_t);
 extern tree build_x_unary_op			(location_t,
-						 enum tree_code, tree,
+						 enum tree_code, cp_expr,
                                                  tsubst_flags_t);
 extern tree cp_build_addr_expr			(tree, tsubst_flags_t);
 extern tree cp_build_unary_op                   (location_t, enum tree_code,
@@ -6597,9 +6663,11 @@ extern tree build_static_cast			(tree, tree, tsubst_flags_t);
 extern tree build_reinterpret_cast		(tree, tree, tsubst_flags_t);
 extern tree build_const_cast			(tree, tree, tsubst_flags_t);
 extern tree build_c_cast			(location_t, tree, tree);
+extern cp_expr build_c_cast			(location_t loc, tree type,
+						 cp_expr expr);
 extern tree cp_build_c_cast			(location_t, tree, tree,
 						 tsubst_flags_t);
-extern tree build_x_modify_expr			(location_t, tree,
+extern cp_expr build_x_modify_expr		(location_t, tree,
 						 enum tree_code, tree,
 						 tsubst_flags_t);
 extern tree cp_build_modify_expr		(location_t, tree,
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index cebe57e..86c07ef 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -5659,7 +5659,7 @@ arg_assoc (struct arg_lookup *k, tree n)
 /* Performs Koenig lookup depending on arguments, where fns
    are the functions found in normal lookup.  */
 
-static tree
+static cp_expr
 lookup_arg_dependent_1 (tree name, tree fns, vec<tree, va_gc> *args)
 {
   struct arg_lookup k;
@@ -5720,10 +5720,10 @@ lookup_arg_dependent_1 (tree name, tree fns, vec<tree, va_gc> *args)
 
 /* Wrapper for lookup_arg_dependent_1.  */
 
-tree
+cp_expr
 lookup_arg_dependent (tree name, tree fns, vec<tree, va_gc> *args)
 {
-  tree ret;
+  cp_expr ret;
   bool subtime;
   subtime = timevar_cond_start (TV_NAME_LOOKUP);
   ret = lookup_arg_dependent_1 (name, fns, args);
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index d430edb..d2453e9 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -347,7 +347,7 @@ extern void do_toplevel_using_decl (tree, tree, tree);
 extern void do_local_using_decl (tree, tree, tree);
 extern tree do_class_using_decl (tree, tree);
 extern void do_using_directive (tree);
-extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
+extern cp_expr lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
 extern bool is_associated_namespace (tree, tree);
 extern void parse_using_directive (tree, tree);
 extern tree innermost_non_namespace_value (tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 88786b1..6f7941d 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -1785,7 +1785,7 @@ struct cp_parser_expression_stack_entry
 {
   /* Left hand side of the binary operation we are currently
      parsing.  */
-  tree lhs;
+  cp_expr lhs;
   /* Original tree code for left hand side, if it was a binary
      expression itself (used for -Wparentheses).  */
   enum tree_code lhs_type;
@@ -1939,15 +1939,15 @@ static cp_parser *cp_parser_new
 
 /* Lexical conventions [gram.lex]  */
 
-static tree cp_parser_identifier
+static cp_expr cp_parser_identifier
   (cp_parser *);
-static tree cp_parser_string_literal
+static cp_expr cp_parser_string_literal
   (cp_parser *, bool, bool, bool);
-static tree cp_parser_userdef_char_literal
+static cp_expr cp_parser_userdef_char_literal
   (cp_parser *);
 static tree cp_parser_userdef_string_literal
   (tree);
-static tree cp_parser_userdef_numeric_literal
+static cp_expr cp_parser_userdef_numeric_literal
   (cp_parser *);
 
 /* Basic concepts [gram.basic]  */
@@ -1957,11 +1957,11 @@ static bool cp_parser_translation_unit
 
 /* Expressions [gram.expr]  */
 
-static tree cp_parser_primary_expression
+static cp_expr cp_parser_primary_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
-static tree cp_parser_id_expression
+static cp_expr cp_parser_id_expression
   (cp_parser *, bool, bool, bool *, bool, bool);
-static tree cp_parser_unqualified_id
+static cp_expr cp_parser_unqualified_id
   (cp_parser *, bool, bool, bool, bool);
 static tree cp_parser_nested_name_specifier_opt
   (cp_parser *, bool, bool, bool, bool);
@@ -1969,19 +1969,19 @@ static tree cp_parser_nested_name_specifier
   (cp_parser *, bool, bool, bool, bool);
 static tree cp_parser_qualifying_entity
   (cp_parser *, bool, bool, bool, bool, bool);
-static tree cp_parser_postfix_expression
+static cp_expr cp_parser_postfix_expression
   (cp_parser *, bool, bool, bool, bool, cp_id_kind *);
 static tree cp_parser_postfix_open_square_expression
   (cp_parser *, tree, bool, bool);
 static tree cp_parser_postfix_dot_deref_expression
-  (cp_parser *, enum cpp_ttype, tree, bool, cp_id_kind *, location_t);
+  (cp_parser *, enum cpp_ttype, cp_expr, bool, cp_id_kind *, location_t);
 static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
-  (cp_parser *, int, bool, bool, bool *);
+  (cp_parser *, int, bool, bool, bool *, location_t * = NULL);
 /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
 enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
 static void cp_parser_pseudo_destructor_name
   (cp_parser *, tree, tree *, tree *);
-static tree cp_parser_unary_expression
+static cp_expr cp_parser_unary_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
 static enum tree_code cp_parser_unary_operator
   (cp_token *);
@@ -1999,23 +1999,23 @@ static vec<tree, va_gc> *cp_parser_new_initializer
   (cp_parser *);
 static tree cp_parser_delete_expression
   (cp_parser *);
-static tree cp_parser_cast_expression
+static cp_expr cp_parser_cast_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
-static tree cp_parser_binary_expression
+static cp_expr cp_parser_binary_expression
   (cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *);
 static tree cp_parser_question_colon_clause
-  (cp_parser *, tree);
-static tree cp_parser_assignment_expression
+  (cp_parser *, cp_expr);
+static cp_expr cp_parser_assignment_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
 static enum tree_code cp_parser_assignment_operator_opt
   (cp_parser *);
-static tree cp_parser_expression
+static cp_expr cp_parser_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
-static tree cp_parser_constant_expression
+static cp_expr cp_parser_constant_expression
   (cp_parser *, bool = false, bool * = NULL);
 static tree cp_parser_builtin_offsetof
   (cp_parser *);
-static tree cp_parser_lambda_expression
+static cp_expr cp_parser_lambda_expression
   (cp_parser *);
 static void cp_parser_lambda_introducer
   (cp_parser *, tree);
@@ -2170,7 +2170,7 @@ static void cp_parser_function_body
   (cp_parser *, bool);
 static tree cp_parser_initializer
   (cp_parser *, bool *, bool *);
-static tree cp_parser_initializer_clause
+static cp_expr cp_parser_initializer_clause
   (cp_parser *, bool *);
 static tree cp_parser_braced_list
   (cp_parser*, bool*);
@@ -2238,9 +2238,9 @@ static tree cp_parser_mem_initializer_id
 
 /* Overloading [gram.over] */
 
-static tree cp_parser_operator_function_id
+static cp_expr cp_parser_operator_function_id
   (cp_parser *);
-static tree cp_parser_operator
+static cp_expr cp_parser_operator
   (cp_parser *);
 
 /* Templates [gram.temp] */
@@ -2411,7 +2411,7 @@ static tree cp_parser_objc_struct_declaration
 
 /* Utility Routines */
 
-static tree cp_parser_lookup_name
+static cp_expr cp_parser_lookup_name
   (cp_parser *, tree, enum tag_types, bool, bool, bool, tree *, location_t);
 static tree cp_parser_lookup_name_simple
   (cp_parser *, tree, location_t);
@@ -2421,7 +2421,7 @@ static bool cp_parser_check_declarator_template_parameters
   (cp_parser *, cp_declarator *, location_t);
 static bool cp_parser_check_template_parameters
   (cp_parser *, unsigned, location_t, cp_declarator *);
-static tree cp_parser_simple_cast_expression
+static cp_expr cp_parser_simple_cast_expression
   (cp_parser *);
 static tree cp_parser_global_scope_opt
   (cp_parser *, bool);
@@ -3670,7 +3670,7 @@ cp_parser_pop_lexer (cp_parser *parser)
 /* Parse an identifier.  Returns an IDENTIFIER_NODE representing the
    identifier.  */
 
-static tree
+static cp_expr
 cp_parser_identifier (cp_parser* parser)
 {
   cp_token *token;
@@ -3678,7 +3678,10 @@ cp_parser_identifier (cp_parser* parser)
   /* Look for the identifier.  */
   token = cp_parser_require (parser, CPP_NAME, RT_NAME);
   /* Return the value.  */
-  return token ? token->u.value : error_mark_node;
+  if (token)
+    return cp_expr (token->u.value, token->location);
+  else
+    return error_mark_node;
 }
 
 /* Parse a sequence of adjacent string constants.  Returns a
@@ -3695,7 +3698,7 @@ cp_parser_identifier (cp_parser* parser)
    This code is largely lifted from lex_string() in c-lex.c.
 
    FUTURE: ObjC++ will need to handle @-strings here.  */
-static tree
+static cp_expr
 cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 			  bool lookup_udlit = true)
 {
@@ -3717,6 +3720,8 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
       return error_mark_node;
     }
 
+  location_t loc = tok->location;
+
   if (cpp_userdef_string_p (tok->type))
     {
       string_tree = USERDEF_LITERAL_VALUE (tok->u.value);
@@ -3754,11 +3759,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
     }
   else
     {
+      location_t last_tok_loc;
       gcc_obstack_init (&str_ob);
       count = 0;
 
       do
 	{
+	  last_tok_loc = tok->location;
 	  cp_lexer_consume_token (parser->lexer);
 	  count++;
 	  str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
@@ -3813,6 +3820,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 	}
       while (cp_parser_is_string_literal (tok));
 
+      /* A string literal built by concatenation has its caret=start at
+	 the start of the initial string, and its finish at the finish of
+	 the final string literal.  */
+      loc = make_location (loc, loc,
+			   get_range_from_loc (line_table,
+					       last_tok_loc).m_finish);
+
       strs = (cpp_string *) obstack_finish (&str_ob);
     }
 
@@ -3865,7 +3879,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
   if (count > 1)
     obstack_free (&str_ob, 0);
 
-  return value;
+  return cp_expr (value, loc);
 }
 
 /* Look up a literal operator with the name and the exact arguments.  */
@@ -3916,7 +3930,7 @@ lookup_literal_operator (tree name, vec<tree, va_gc> *args)
 /* Parse a user-defined char constant.  Returns a call to a user-defined
    literal operator taking the character as an argument.  */
 
-static tree
+static cp_expr
 cp_parser_userdef_char_literal (cp_parser *parser)
 {
   cp_token *token = cp_lexer_consume_token (parser->lexer);
@@ -4008,7 +4022,7 @@ make_string_pack (tree value)
 /* Parse a user-defined numeric constant.  returns a call to a user-defined
    literal operator.  */
 
-static tree
+static cp_expr
 cp_parser_userdef_numeric_literal (cp_parser *parser)
 {
   cp_token *token = cp_lexer_consume_token (parser->lexer);
@@ -4409,7 +4423,7 @@ cp_parser_fold_operator (cp_parser *parser)
 
    Note that the '(' and ')' are matched in primary expression. */
 
-static tree
+static cp_expr
 cp_parser_fold_expression (cp_parser *parser, tree expr1)
 {
   cp_id_kind pidk;
@@ -4529,7 +4543,7 @@ cp_parser_fold_expression (cp_parser *parser, tree expr1)
    Returns a representation of the expression.  Upon return, *IDK
    indicates what kind of id-expression (if any) was present.  */
 
-static tree
+static cp_expr
 cp_parser_primary_expression (cp_parser *parser,
 			      bool address_p,
 			      bool cast_p,
@@ -4614,7 +4628,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	  if (!cast_p)
 	    cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
 	}
-      return token->u.value;
+      return cp_expr (token->u.value, token->location);
 
     case CPP_CHAR_USERDEF:
     case CPP_CHAR16_USERDEF:
@@ -4672,9 +4686,11 @@ cp_parser_primary_expression (cp_parser *parser,
 	}
       /* Otherwise it's a normal parenthesized expression.  */
       {
-	tree expr;
+	cp_expr expr;
 	bool saved_greater_than_is_operator_p;
 
+	location_t open_paren_loc = token->location;
+
 	/* Consume the `('.  */
 	cp_lexer_consume_token (parser->lexer);
 	/* Within a parenthesized expression, a `>' token is always
@@ -4719,7 +4735,11 @@ cp_parser_primary_expression (cp_parser *parser,
 	   template-parameter-list now.  */
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
+
 	/* Consume the `)'.  */
+	token = cp_lexer_peek_token (parser->lexer);
+	location_t close_paren_loc = token->location;
+	expr.set_range (open_paren_loc, close_paren_loc);
 	if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
 	    && !cp_parser_uncommitted_to_tentative_parse_p (parser))
 	  cp_parser_skip_to_end_of_statement (parser);
@@ -4739,7 +4759,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	      return msg;
 	    /* ... else, fall though to see if it's a lambda.  */
 	  }
-	tree lam = cp_parser_lambda_expression (parser);
+	cp_expr lam = cp_parser_lambda_expression (parser);
 	/* Don't warn about a failed tentative parse.  */
 	if (cp_parser_error_occurred (parser))
 	  return error_mark_node;
@@ -4760,20 +4780,20 @@ cp_parser_primary_expression (cp_parser *parser,
 	  /* These two are the boolean literals.  */
 	case RID_TRUE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_true_node;
+	  return cp_expr (boolean_true_node, token->location);
 	case RID_FALSE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_false_node;
+	  return cp_expr (boolean_false_node, token->location);
 
 	  /* The `__null' literal.  */
 	case RID_NULL:
 	  cp_lexer_consume_token (parser->lexer);
-	  return null_node;
+	  return cp_expr (null_node, token->location);
 
 	  /* The `nullptr' literal.  */
 	case RID_NULLPTR:
 	  cp_lexer_consume_token (parser->lexer);
-	  return nullptr_node;
+	  return cp_expr (nullptr_node, token->location);
 
 	  /* Recognize the `this' keyword.  */
 	case RID_THIS:
@@ -4921,14 +4941,14 @@ cp_parser_primary_expression (cp_parser *parser,
     case CPP_TEMPLATE_ID:
     case CPP_NESTED_NAME_SPECIFIER:
       {
-	tree id_expression;
-	tree decl;
+      id_expression:
+	cp_expr id_expression;
+	cp_expr decl;
 	const char *error_msg;
 	bool template_p;
 	bool done;
 	cp_token *id_expr_token;
 
-      id_expression:
 	/* Parse the id-expression.  */
 	id_expression
 	  = cp_parser_id_expression (parser,
@@ -4997,8 +5017,20 @@ cp_parser_primary_expression (cp_parser *parser,
 	      }
 
 	    /* In Objective-C++, an instance variable (ivar) may be preferred
-	       to whatever cp_parser_lookup_name() found.  */
-	    decl = objc_lookup_ivar (decl, id_expression);
+	       to whatever cp_parser_lookup_name() found.
+	       Call objc_lookup_ivar.  To avoid exposing cp_expr to the
+	       rest of c-family, we have to do a little extra work to preserve
+	       any location information in cp_expr "decl".  Given that
+	       objc_lookup_ivar is implemented in "c-family" and "objc", we
+	       have a trip through the pure "tree" type, rather than cp_expr.
+	       Naively copying it back to "decl" would implicitly give the
+	       new cp_expr value an UNKNOWN_LOCATION for nodes that don't
+	       store an EXPR_LOCATION.  Hence we only update "decl" (and
+	       hence its location_t) if we get back a different tree node.  */
+	    tree decl_tree = objc_lookup_ivar (decl.get_value (),
+					       id_expression);
+	    if (decl_tree != decl.get_value ())
+	      decl = cp_expr (decl_tree);
 
 	    /* If name lookup gives us a SCOPE_REF, then the
 	       qualifying scope was dependent.  */
@@ -5039,7 +5071,7 @@ cp_parser_primary_expression (cp_parser *parser,
 		  {
 		    error_at (id_expr_token->location,
 			      "local variable %qD may not appear in this context",
-			      decl);
+			      decl.get_value ());
 		    return error_mark_node;
 		  }
 	      }
@@ -5067,7 +5099,7 @@ cp_parser_primary_expression (cp_parser *parser,
     }
 }
 
-static inline tree
+static inline cp_expr
 cp_parser_primary_expression (cp_parser *parser,
 			      bool address_p,
 			      bool cast_p,
@@ -5112,7 +5144,7 @@ cp_parser_primary_expression (cp_parser *parser,
    If DECLARATOR_P is true, the id-expression is appearing as part of
    a declarator, rather than as part of an expression.  */
 
-static tree
+static cp_expr
 cp_parser_id_expression (cp_parser *parser,
 			 bool template_keyword_p,
 			 bool check_dependency_p,
@@ -5247,7 +5279,7 @@ cp_parser_id_expression (cp_parser *parser,
    is true, the unqualified-id is appearing as part of a declarator,
    rather than as part of an expression.  */
 
-static tree
+static cp_expr
 cp_parser_unqualified_id (cp_parser* parser,
 			  bool template_keyword_p,
 			  bool check_dependency_p,
@@ -5510,7 +5542,7 @@ cp_parser_unqualified_id (cp_parser* parser,
     case CPP_KEYWORD:
       if (token->keyword == RID_OPERATOR)
 	{
-	  tree id;
+	  cp_expr id;
 
 	  /* This could be a template-id, so we try that first.  */
 	  cp_parser_parse_tentatively (parser);
@@ -6100,7 +6132,7 @@ cp_parser_compound_literal_p (cp_parser *parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
                               bool member_access_only_p, bool decltype_p,
 			      cp_id_kind * pidk_return)
@@ -6109,13 +6141,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
   location_t loc;
   enum rid keyword;
   cp_id_kind idk = CP_ID_KIND_NONE;
-  tree postfix_expression = NULL_TREE;
+  cp_expr postfix_expression = NULL_TREE;
   bool is_member_access = false;
   int saved_in_statement = -1;
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
   loc = token->location;
+  location_t start_loc = get_range_from_loc (line_table, loc).m_start;
+
   /* Some of the productions are determined by keywords.  */
   keyword = token->keyword;
   switch (keyword)
@@ -6126,7 +6160,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
     case RID_CONSTCAST:
       {
 	tree type;
-	tree expression;
+	cp_expr expression;
 	const char *saved_message;
 	bool saved_in_type_id_in_expr_p;
 
@@ -6159,7 +6193,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* And the expression which is being cast.  */
 	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
 	expression = cp_parser_expression (parser, & idk, /*cast_p=*/true);
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN,
+						   RT_CLOSE_PAREN);
+	location_t end_loc = close_paren ?
+	  close_paren->location : UNKNOWN_LOCATION;
 
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
@@ -6192,6 +6229,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  default:
 	    gcc_unreachable ();
 	  }
+
+	/* Construct a location e.g. :
+	     reinterpret_cast <int *> (expr)
+	     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	   ranging from the start of the "*_cast" token to the final closing
+	   paren, with the caret at the start.  */
+	location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc);
+	postfix_expression.set_location (cp_cast_loc);
       }
       break;
 
@@ -6471,6 +6516,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 							postfix_expression,
 							false,
 							decltype_p);
+          postfix_expression.set_range (start_loc,
+                                        postfix_expression.get_location ());
+
 	  idk = CP_ID_KIND_NONE;
           is_member_access = false;
 	  break;
@@ -6484,6 +6532,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    bool saved_non_integral_constant_expression_p = false;
 	    tsubst_flags_t complain = complain_flags (decltype_p);
 	    vec<tree, va_gc> *args;
+	    location_t close_paren_loc;
 
             is_member_access = false;
 
@@ -6502,7 +6551,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    args = (cp_parser_parenthesized_expression_list
 		    (parser, non_attr,
 		     /*cast_p=*/false, /*allow_expansion_p=*/true,
-		     /*non_constant_p=*/NULL));
+		     /*non_constant_p=*/NULL,
+		     /*close_paren_loc=*/&close_paren_loc));
 	    if (is_builtin_constant_p)
 	      {
 		parser->integral_constant_expression_p
@@ -6644,7 +6694,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 				    koenig_p,
 				    complain);
 
-	    protected_set_expr_location (postfix_expression, token->location);
+	    location_t combined_loc = make_location (token->location,
+						     start_loc,
+						     close_paren_loc);
+	    protected_set_expr_location (postfix_expression, combined_loc);
 
 	    /* The POSTFIX_EXPRESSION is certainly no longer an id.  */
 	    idk = CP_ID_KIND_NONE;
@@ -6705,7 +6758,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  if (pidk_return != NULL)
 	    * pidk_return = idk;
           if (member_access_only_p)
-            return is_member_access? postfix_expression : error_mark_node;
+            return is_member_access
+              ? postfix_expression
+              : cp_expr (error_mark_node);
           else
             return postfix_expression;
 	}
@@ -6905,7 +6960,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 static tree
 cp_parser_postfix_dot_deref_expression (cp_parser *parser,
 					enum cpp_ttype token_type,
-					tree postfix_expression,
+					cp_expr postfix_expression,
 					bool for_offsetof, cp_id_kind *idk,
 					location_t location)
 {
@@ -6942,7 +6997,7 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
       if (scope == unknown_type_node)
 	{
 	  error_at (location, "%qE does not have class type",
-		    postfix_expression);
+		    postfix_expression.get_value ());
 	  scope = NULL_TREE;
 	}
       /* Unlike the object expression in other contexts, *this is not
@@ -7107,7 +7162,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 int is_attribute_list,
 					 bool cast_p,
                                          bool allow_expansion_p,
-					 bool *non_constant_p)
+					 bool *non_constant_p,
+					 location_t *close_paren_loc)
 {
   vec<tree, va_gc> *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
@@ -7211,6 +7267,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 	cp_lexer_consume_token (parser->lexer);
       }
 
+  if (close_paren_loc)
+    *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
     {
       int ending;
@@ -7380,7 +7439,7 @@ cp_parser_pseudo_destructor_name (cp_parser* parser,
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 			    bool address_p, bool cast_p, bool decltype_p)
 {
@@ -7594,8 +7653,8 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
     }
   if (unary_operator != ERROR_MARK)
     {
-      tree cast_expression;
-      tree expression = error_mark_node;
+      cp_expr cast_expression;
+      cp_expr expression = error_mark_node;
       non_integral_constant non_constant_p = NIC_NONE;
       location_t loc = token->location;
       tsubst_flags_t complain = complain_flags (decltype_p);
@@ -7611,6 +7670,14 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 				     /*cast_p=*/false,
 				     /*decltype*/false,
 				     pidk);
+
+      /* Make a location:
+	    OP_TOKEN  CAST_EXPRESSION
+	    ^~~~~~~~~~~~~~~~~~~~~~~~~
+	 with start==caret at the operator token, and
+	 extending to the end of the cast_expression.  */
+      loc = make_location (loc, loc, cast_expression.get_finish ());
+
       /* Now, build an appropriate representation.  */
       switch (unary_operator)
 	{
@@ -7726,6 +7793,8 @@ cp_parser_new_expression (cp_parser* parser)
   tree nelts = NULL_TREE;
   tree ret;
 
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   /* Look for the optional `::' operator.  */
   global_scope_p
     = (cp_parser_global_scope_opt (parser,
@@ -7810,8 +7879,18 @@ cp_parser_new_expression (cp_parser* parser)
     }
   else
     {
+      /* Construct a location e.g.:
+           ptr = new int[100]
+                 ^~~~~~~~~~~~
+         with caret == start at the start of the "new" token, and the end
+         at the end of the final token we consumed.  */
+      cp_token *end_tok = cp_lexer_previous_token (parser->lexer);
+      location_t end_loc = get_range_from_loc (line_table,
+                                               end_tok->location).m_finish;
+      location_t combined_loc = make_location (start_loc, start_loc, end_loc);
+
       /* Create a representation of the new-expression.  */
-      ret = build_new (input_location, &placement, type, nelts, &initializer,
+      ret = build_new (combined_loc, &placement, type, nelts, &initializer,
                        global_scope_p, tf_warning_or_error);
     }
 
@@ -8192,7 +8271,7 @@ cp_parser_tokens_start_cast_expression (cp_parser *parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 			   bool decltype_p, cp_id_kind * pidk)
 {
@@ -8200,7 +8279,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
       tree type = NULL_TREE;
-      tree expr = NULL_TREE;
+      cp_expr expr (NULL_TREE);
       int cast_expression = 0;
       const char *saved_message;
 
@@ -8213,7 +8292,9 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
       parser->type_definition_forbidden_message
 	= G_("types may not be defined in casts");
       /* Consume the `('.  */
-      cp_lexer_consume_token (parser->lexer);
+      cp_token *open_paren = cp_lexer_consume_token (parser->lexer);
+      location_t open_paren_loc = open_paren->location;
+
       /* A very tricky bit is that `(struct S) { 3 }' is a
 	 compound-literal (which we permit in C++ as an extension).
 	 But, that construct is not a cast-expression -- it is a
@@ -8315,7 +8396,15 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 		return error_mark_node;
 
 	      /* Perform the cast.  */
-	      expr = build_c_cast (input_location, type, expr);
+	      /* Make a location:
+		   (TYPE) EXPR
+		   ^~~~~~~~~~~
+		 with start==caret at the open paren, extending to the
+		 end of "expr".  */
+	      location_t cast_loc = make_location (open_paren_loc,
+						   open_paren_loc,
+						   expr.get_finish ());
+	      expr = build_c_cast (cast_loc, type, expr);
 	      return expr;
 	    }
 	}
@@ -8408,7 +8497,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
  ? PREC_NOT_OPERATOR					     \
  : binops_by_token[token->type].prec)
 
-static tree
+static cp_expr
 cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 			     bool no_toplevel_fold_p,
 			     bool decltype_p,
@@ -8418,7 +8507,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
   cp_parser_expression_stack stack;
   cp_parser_expression_stack_entry *sp = &stack[0];
   cp_parser_expression_stack_entry current;
-  tree rhs;
+  cp_expr rhs;
   cp_token *token;
   enum tree_code rhs_type;
   enum cp_parser_prec new_prec, lookahead_prec;
@@ -8558,6 +8647,11 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 				      maybe_constant_value (rhs));
 
       overload = NULL;
+
+      location_t combined_loc = make_location (current.loc,
+					       current.lhs.get_start (),
+					       rhs.get_finish ());
+
       /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type ==
 	 ERROR_MARK for everything that is not a binary expression.
 	 This makes warn_about_parentheses miss some warnings that
@@ -8568,18 +8662,18 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
       if (no_toplevel_fold_p
 	  && lookahead_prec <= current.prec
 	  && sp == stack)
-	current.lhs = build2 (current.tree_type,
-			      TREE_CODE_CLASS (current.tree_type)
-			      == tcc_comparison
-			      ? boolean_type_node : TREE_TYPE (current.lhs),
-			      current.lhs, rhs);
+	current.lhs = build2_loc (combined_loc,
+				  current.tree_type,
+				  TREE_CODE_CLASS (current.tree_type)
+				  == tcc_comparison
+				  ? boolean_type_node : TREE_TYPE (current.lhs),
+				  current.lhs, rhs);
       else
-	current.lhs = build_x_binary_op (current.loc, current.tree_type,
+	current.lhs = build_x_binary_op (combined_loc, current.tree_type,
 					 current.lhs, current.lhs_type,
 					 rhs, rhs_type, &overload,
 					 complain_flags (decltype_p));
       current.lhs_type = current.tree_type;
-      protected_set_expr_location (current.lhs, current.loc);
 
       /* If the binary operator required the use of an overloaded operator,
 	 then this expression cannot be an integral constant-expression.
@@ -8596,7 +8690,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
   return current.lhs;
 }
 
-static tree
+static cp_expr
 cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 			     bool no_toplevel_fold_p,
 			     enum cp_parser_prec prec,
@@ -8620,10 +8714,10 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
      ? : assignment-expression */
 
 static tree
-cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
+cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr)
 {
   tree expr, folded_logical_or_expr = cp_fully_fold (logical_or_expr);
-  tree assignment_expr;
+  cp_expr assignment_expr;
   struct cp_token *token;
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
@@ -8662,6 +8756,15 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
   c_inhibit_evaluation_warnings -=
     folded_logical_or_expr == truthvalue_true_node;
 
+  /* Make a location:
+       LOGICAL_OR_EXPR ? EXPR : ASSIGNMENT_EXPR
+       ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
+     with the caret at the "?", ranging from the start of
+     the logical_or_expr to the end of the assignment_expr.  */
+  loc = make_location (loc,
+		       logical_or_expr.get_start (),
+		       assignment_expr.get_finish ());
+
   /* Build the conditional-expression.  */
   return build_x_conditional_expr (loc, logical_or_expr,
 				   expr,
@@ -8681,11 +8784,11 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
 
    Returns a representation for the expression.  */
 
-static tree
+static cp_expr
 cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 				 bool cast_p, bool decltype_p)
 {
-  tree expr;
+  cp_expr expr;
 
   /* If the next token is the `throw' keyword, then we're looking at
      a throw-expression.  */
@@ -8716,7 +8819,8 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 	      bool non_constant_p;
 
 	      /* Parse the right-hand side of the assignment.  */
-	      tree rhs = cp_parser_initializer_clause (parser, &non_constant_p);
+	      cp_expr rhs = cp_parser_initializer_clause (parser,
+							  &non_constant_p);
 
 	      if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
 		maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
@@ -8727,7 +8831,15 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 							      NIC_ASSIGNMENT))
 		return error_mark_node;
 	      /* Build the assignment expression.  Its default
-		 location is the location of the '=' token.  */
+		 location:
+		   LHS = RHS
+		   ~~~~^~~~~
+		 is the location of the '=' token as the
+		 caret, ranging from the start of the lhs to the
+		 end of the rhs.  */
+	      loc = make_location (loc,
+				   expr.get_start (),
+				   rhs.get_finish ());
 	      expr = build_x_modify_expr (loc, expr,
 					  assignment_operator,
 					  rhs,
@@ -8840,16 +8952,16 @@ cp_parser_assignment_operator_opt (cp_parser* parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
 		      bool cast_p, bool decltype_p)
 {
-  tree expression = NULL_TREE;
+  cp_expr expression = NULL_TREE;
   location_t loc = UNKNOWN_LOCATION;
 
   while (true)
     {
-      tree assignment_expression;
+      cp_expr assignment_expression;
 
       /* Parse the next assignment-expression.  */
       assignment_expression
@@ -8871,9 +8983,17 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
       if (!expression)
 	expression = assignment_expression;
       else
-	expression = build_x_compound_expr (loc, expression,
-					    assignment_expression,
-					    complain_flags (decltype_p));
+	{
+	  /* Create a location with caret at the comma, ranging
+	     from the start of the LHS to the end of the RHS.  */
+	  loc = make_location (loc,
+			       expression.get_start (),
+			       assignment_expression.get_finish ());
+	  expression = build_x_compound_expr (loc, expression,
+					      assignment_expression,
+					      complain_flags (decltype_p));
+	  expression.set_location (loc);
+	}
       /* If the next token is not a comma, or we're in a fold-expression, then
 	 we are done with the expression.  */
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)
@@ -8900,7 +9020,7 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
   constant, *NON_CONSTANT_P is set to TRUE.  If ALLOW_NON_CONSTANT_P
   is false, NON_CONSTANT_P should be NULL.  */
 
-static tree
+static cp_expr
 cp_parser_constant_expression (cp_parser* parser,
 			       bool allow_non_constant_p,
 			       bool *non_constant_p)
@@ -8908,7 +9028,7 @@ cp_parser_constant_expression (cp_parser* parser,
   bool saved_integral_constant_expression_p;
   bool saved_allow_non_integral_constant_expression_p;
   bool saved_non_integral_constant_expression_p;
-  tree expression;
+  cp_expr expression;
 
   /* It might seem that we could simply parse the
      conditional-expression, and then check to see if it were
@@ -9278,7 +9398,7 @@ finish_lambda_scope (void)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_lambda_expression (cp_parser* parser)
 {
   tree lambda_expr = build_lambda_expr ();
@@ -13280,7 +13400,7 @@ cp_parser_mem_initializer_id (cp_parser* parser)
    Returns an IDENTIFIER_NODE for the operator which is a
    human-readable spelling of the identifier, e.g., `operator +'.  */
 
-static tree
+static cp_expr
 cp_parser_operator_function_id (cp_parser* parser)
 {
   /* Look for the `operator' keyword.  */
@@ -13320,7 +13440,7 @@ cp_literal_operator_id (const char* name)
    Returns an IDENTIFIER_NODE for the operator which is a
    human-readable spelling of the identifier, e.g., `operator +'.  */
 
-static tree
+static cp_expr
 cp_parser_operator (cp_parser* parser)
 {
   tree id = NULL_TREE;
@@ -13329,6 +13449,9 @@ cp_parser_operator (cp_parser* parser)
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
+
+  location_t start_loc = token->location;
+
   /* Figure out which operator we have.  */
   switch (token->type)
     {
@@ -13345,7 +13468,7 @@ cp_parser_operator (cp_parser* parser)
 	  break;
 
 	/* Consume the `new' or `delete' token.  */
-	cp_lexer_consume_token (parser->lexer);
+	location_t end_loc = cp_lexer_consume_token (parser->lexer)->location;
 
 	/* Peek at the next token.  */
 	token = cp_lexer_peek_token (parser->lexer);
@@ -13356,7 +13479,8 @@ cp_parser_operator (cp_parser* parser)
 	    /* Consume the `[' token.  */
 	    cp_lexer_consume_token (parser->lexer);
 	    /* Look for the `]' token.  */
-	    cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
+	    end_loc = cp_parser_require (parser, CPP_CLOSE_SQUARE,
+                                         RT_CLOSE_SQUARE)->location;
 	    id = ansi_opname (op == NEW_EXPR
 			      ? VEC_NEW_EXPR : VEC_DELETE_EXPR);
 	  }
@@ -13364,7 +13488,9 @@ cp_parser_operator (cp_parser* parser)
 	else
 	  id = ansi_opname (op);
 
-	return id;
+	location_t loc = make_location (start_loc, start_loc, end_loc);
+
+	return cp_expr (id, loc);
       }
 
     case CPP_PLUS:
@@ -13610,7 +13736,7 @@ cp_parser_operator (cp_parser* parser)
       id = error_mark_node;
     }
 
-  return id;
+  return cp_expr (id, start_loc);
 }
 
 /* Parse a template-declaration.
@@ -20303,10 +20429,10 @@ cp_parser_initializer (cp_parser* parser, bool* is_direct_init,
 
    Otherwise, calls cp_parser_braced_list.  */
 
-static tree
+static cp_expr
 cp_parser_initializer_clause (cp_parser* parser, bool* non_constant_p)
 {
-  tree initializer;
+  cp_expr initializer;
 
   /* Assume the expression is constant.  */
   *non_constant_p = false;
@@ -24072,7 +24198,7 @@ cp_parser_nested_requirement (cp_parser *parser)
    TREE_LIST of candidates if name-lookup results in an ambiguity, and
    NULL_TREE otherwise.  */
 
-static tree
+static cp_expr
 cp_parser_lookup_name (cp_parser *parser, tree name,
 		       enum tag_types tag_type,
 		       bool is_template,
@@ -24314,7 +24440,7 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
 
   maybe_record_typedef_use (decl);
 
-  return decl;
+  return cp_expr (decl, name_location);
 }
 
 /* Like cp_parser_lookup_name, but for use in the typical case where
@@ -25318,7 +25444,7 @@ cp_parser_single_declaration (cp_parser* parser,
 
 /* Parse a cast-expression that is not the operand of a unary "&".  */
 
-static tree
+static cp_expr
 cp_parser_simple_cast_expression (cp_parser *parser)
 {
   return cp_parser_cast_expression (parser, /*address_p=*/false,
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index ead2bba..63252a5 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2165,8 +2165,8 @@ empty_expr_stmt_p (tree expr_stmt)
    the function (or functions) to call; ARGS are the arguments to the
    call.  Returns the functions to be considered by overload resolution.  */
 
-tree
-perform_koenig_lookup (tree fn, vec<tree, va_gc> *args,
+cp_expr
+perform_koenig_lookup (cp_expr fn, vec<tree, va_gc> *args,
 		       tsubst_flags_t complain)
 {
   tree identifier = NULL_TREE;
@@ -2461,10 +2461,15 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
    is indicated by CODE, which should be POSTINCREMENT_EXPR or
    POSTDECREMENT_EXPR.)  */
 
-tree
-finish_increment_expr (tree expr, enum tree_code code)
+cp_expr
+finish_increment_expr (cp_expr expr, enum tree_code code)
 {
-  return build_x_unary_op (input_location, code, expr, tf_warning_or_error);
+  /* input_location holds the location of the trailing operator token.  */
+  cp_expr result = build_x_unary_op (input_location, code, expr,
+				     tf_warning_or_error);
+  result.set_range (expr.get_start (),
+		    get_range_from_loc (line_table, input_location).m_finish);
+  return result;
 }
 
 /* Finish a use of `this'.  Returns an expression for `this'.  */
@@ -2558,15 +2563,17 @@ finish_pseudo_destructor_expr (tree object, tree scope, tree destructor,
 
 /* Finish an expression of the form CODE EXPR.  */
 
-tree
-finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
+cp_expr
+finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr,
 		      tsubst_flags_t complain)
 {
-  tree result = build_x_unary_op (loc, code, expr, complain);
+  location_t combined_loc = make_location (op_loc,
+					   op_loc, expr.get_finish ());
+  tree result = build_x_unary_op (combined_loc, code, expr, complain);
   tree result_ovl, expr_ovl;
 
   if (!(complain & tf_warning))
-    return result;
+    return cp_expr (result, combined_loc);
 
   result_ovl = result;
   expr_ovl = expr;
@@ -2576,15 +2583,15 @@ finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
 
   if (!CONSTANT_CLASS_P (expr_ovl)
       || TREE_OVERFLOW_P (expr_ovl))
-    return result;
+    return cp_expr (result, combined_loc);
 
   if (!processing_template_decl)
     result_ovl = cp_fully_fold (result_ovl);
 
   if (CONSTANT_CLASS_P (result_ovl) && TREE_OVERFLOW_P (result_ovl))
-    overflow_warning (input_location, result_ovl);
+    overflow_warning (combined_loc, result_ovl);
 
-  return result;
+  return cp_expr (result, combined_loc);
 }
 
 /* Finish a compound-literal expression.  TYPE is the type to which
@@ -3326,7 +3333,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
    the use of "this" explicit.
 
    Upon return, *IDK will be filled in appropriately.  */
-tree
+cp_expr
 finish_id_expression (tree id_expression,
 		      tree decl,
 		      tree scope,
@@ -3671,7 +3678,7 @@ finish_id_expression (tree id_expression,
 	}
     }
 
-  return decl;
+  return cp_expr (decl, location);
 }
 
 /* Implement the __typeof keyword: Return the type of EXPR, suitable for
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index cc98813..5c00f31 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2261,7 +2261,7 @@ lookup_anon_field (tree t, tree type)
    functions indicated by MEMBER.  */
 
 tree
-build_class_member_access_expr (tree object, tree member,
+build_class_member_access_expr (cp_expr object, tree member,
 				tree access_path, bool preserve_reference,
 				tsubst_flags_t complain)
 {
@@ -2291,10 +2291,10 @@ build_class_member_access_expr (tree object, tree member,
 	      && CLASS_TYPE_P (TREE_TYPE (object_type)))
 	    error ("request for member %qD in %qE, which is of pointer "
 		   "type %qT (maybe you meant to use %<->%> ?)",
-		   member, object, object_type);
+		   member, object.get_value (), object_type);
 	  else
 	    error ("request for member %qD in %qE, which is of non-class "
-		   "type %qT", member, object, object_type);
+		   "type %qT", member, object.get_value (), object_type);
 	}
       return error_mark_node;
     }
@@ -2436,7 +2436,12 @@ build_class_member_access_expr (tree object, tree member,
 	  member_type = cp_build_qualified_type (member_type, type_quals);
 	}
 
-      result = build3_loc (input_location, COMPONENT_REF, member_type,
+      location_t combined_loc =
+	make_location (input_location,
+		       object.get_start (),
+		       get_range_from_loc (line_table,
+					   input_location).m_finish);
+      result = build3_loc (combined_loc, COMPONENT_REF, member_type,
 			   object, member, NULL_TREE);
 
       /* Mark the expression const or volatile, as appropriate.  Even
@@ -2625,7 +2630,7 @@ check_template_keyword (tree decl)
    be a template via the use of the "A::template B" syntax.  */
 
 tree
-finish_class_member_access_expr (tree object, tree name, bool template_p,
+finish_class_member_access_expr (cp_expr object, tree name, bool template_p,
 				 tsubst_flags_t complain)
 {
   tree expr;
@@ -2659,7 +2664,7 @@ finish_class_member_access_expr (tree object, tree name, bool template_p,
 	      && TYPE_P (TREE_OPERAND (name, 0))
 	      && dependent_type_p (TREE_OPERAND (name, 0))))
 	return build_min_nt_loc (UNKNOWN_LOCATION, COMPONENT_REF,
-				 object, name, NULL_TREE);
+				 object.get_value (), name, NULL_TREE);
       object = build_non_dependent_expr (object);
     }
   else if (c_dialect_objc ()
@@ -2682,10 +2687,10 @@ finish_class_member_access_expr (tree object, tree name, bool template_p,
 	      && CLASS_TYPE_P (TREE_TYPE (object_type)))
 	    error ("request for member %qD in %qE, which is of pointer "
 		   "type %qT (maybe you meant to use %<->%> ?)",
-		   name, object, object_type);
+		   name, object.get_value (), object_type);
 	  else
 	    error ("request for member %qD in %qE, which is of non-class "
-		   "type %qT", name, object, object_type);
+		   "type %qT", name, object.get_value (), object_type);
 	}
       return error_mark_node;
     }
@@ -5107,7 +5112,7 @@ cp_build_binary_op (location_t location,
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
-  result = build2 (resultcode, build_type, op0, op1);
+  result = build2_loc (location, resultcode, build_type, op0, op1);
   if (final_type != 0)
     result = cp_convert (final_type, result, complain);
 
@@ -5265,7 +5270,7 @@ pointer_diff (tree op0, tree op1, tree ptrtype, tsubst_flags_t complain)
    and XARG is the operand.  */
 
 tree
-build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
+build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
 		  tsubst_flags_t complain)
 {
   tree orig_expr = xarg;
@@ -5275,7 +5280,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
   if (processing_template_decl)
     {
       if (type_dependent_expression_p (xarg))
-	return build_min_nt_loc (loc, code, xarg, NULL_TREE);
+	return build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE);
 
       xarg = build_non_dependent_expr (xarg);
     }
@@ -5310,7 +5315,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 		error (DECL_CONSTRUCTOR_P (fn)
 		       ? G_("taking address of constructor %qE")
 		       : G_("taking address of destructor %qE"),
-		       xarg);
+		       xarg.get_value ());
 	      return error_mark_node;
 	    }
 	}
@@ -5326,7 +5331,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 	      if (complain & tf_error)
 		{
 		  error ("invalid use of %qE to form a "
-			 "pointer-to-member-function", xarg);
+			 "pointer-to-member-function", xarg.get_value ());
 		  if (TREE_CODE (xarg) != OFFSET_REF)
 		    inform (input_location, "  a qualified-id is required");
 		}
@@ -5337,7 +5342,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 	      if (complain & tf_error)
 		error ("parentheses around %qE cannot be used to form a"
 		       " pointer-to-member-function",
-		       xarg);
+		       xarg.get_value ());
 	      else
 		return error_mark_node;
 	      PTRMEM_OK_P (xarg) = 1;
@@ -7299,6 +7304,17 @@ build_c_cast (location_t loc, tree type, tree expr)
   return cp_build_c_cast (loc, type, expr, tf_warning_or_error);
 }
 
+/* Like the "build_c_cast" used for c-common, but using cp_expr to
+   preserve location information even for tree nodes that don't
+   support it.  */
+
+cp_expr
+build_c_cast (location_t loc, tree type, cp_expr expr)
+{
+  tree result = cp_build_c_cast (loc, type, expr, tf_warning_or_error);
+  return cp_expr (result, loc);
+}
+
 /* Build an expression representing an explicit C-style cast to type
    TYPE of expression EXPR.  */
 
@@ -7808,7 +7824,7 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
   return result;
 }
 
-tree
+cp_expr
 build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 		     tree rhs, tsubst_flags_t complain)
 {
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
new file mode 100644
index 0000000..bba79e3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
@@ -0,0 +1,532 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret" } */
+
+/* This is a collection of unittests to verify that we're correctly
+   capturing the source code ranges of various kinds of expression.
+
+   It uses the various "diagnostic_test_*_expression_range_plugin"
+   plugins which handles "__emit_expression_range" by generating a warning
+   at the given source range of the input argument.  Each of the
+   different plugins do this at a different phase of the internal
+   representation (tree, gimple, etc), so we can verify that the
+   source code range information is valid at each phase.
+
+   We want to accept an expression of any type.  We use variadic arguments.
+   For compatibility with the C tests we have a dummy argument, since
+   C requires at least one argument before the ellipsis.  */
+
+extern void __emit_expression_range (int dummy, ...);
+
+int global;
+
+void test_parentheses (int a, int b)
+{
+  __emit_expression_range (0, (a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) );
+                               ~~~^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, (a + b) * (a - b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) * (a - b) );
+                               ~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, !(a && b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !(a && b) );
+                               ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Postfix expressions.  ************************************************/
+
+void test_array_reference (int *arr)
+{
+  __emit_expression_range (0, arr[100] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, arr[100] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+}
+
+int test_function_call (int p, int q, int r)
+{
+  __emit_expression_range (0, test_function_call (p, q, r) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, test_function_call (p, q, r) );
+                               ~~~~~~~~~~~~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+  return 0;
+}
+
+struct test_struct
+{
+  int field;
+};
+
+int test_structure_references (struct test_struct *ptr)
+{
+  struct test_struct local;
+  local.field = 42;
+
+  __emit_expression_range (0, local.field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, local.field );
+                               ~~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, ptr->field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ptr->field );
+                               ~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+int test_postfix_incdec (int i)
+{
+  __emit_expression_range (0, i++ ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i++ );
+                               ~^~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, i-- ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i-- );
+                               ~^~
+   { dg-end-multiline-output "" } */
+}
+
+/* Unary operators.  ****************************************************/
+
+int test_prefix_incdec (int i)
+{
+  __emit_expression_range (0, ++i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ++i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, --i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, --i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_address_operator (void)
+{
+  __emit_expression_range (0, &global ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &global );
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_indirection (int *ptr)
+{
+  __emit_expression_range (0, *ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *ptr );
+                               ^~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_unary_minus (int i)
+{
+  __emit_expression_range (0, -i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, -i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_ones_complement (int i)
+{
+  __emit_expression_range (0, ~i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ~i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_negation (int flag)
+{
+  __emit_expression_range (0, !flag ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !flag );
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Casts.  ****************************************************/
+
+void test_cast (void *ptr)
+{
+  __emit_expression_range (0, (int *)ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (int *)ptr );
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, *(int *)0xdeadbeef ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *(int *)0xdeadbeef );
+                               ^~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* Binary operators.  *******************************************/
+
+void test_multiplicative_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs * rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs * rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs / rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs / rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs % rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs % rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_additive_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs + rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs + rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs - rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs - rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_shift_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs << rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs << rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >> rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >> rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_relational_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs < rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs < rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs > rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs > rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs <= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs <= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_equality_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs == rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs == rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs != rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs != rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_bitwise_binary_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs & rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs & rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs ^ rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs ^ rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs | rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs | rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs && rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs && rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs || rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs || rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Conditional operator.  *******************************************/
+
+void test_conditional_operators (int flag, int on_true, int on_false)
+{
+  __emit_expression_range (0, flag ? on_true : on_false ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, flag ? on_true : on_false );
+                               ~~~~~^~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Assignment expressions.  *******************************************/
+
+void test_assignment_expressions (int dest, int other)
+{
+  __emit_expression_range (0, dest = other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest = other );
+                               ~~~~~^~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest *= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest *= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest /= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest /= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest %= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest %= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest += other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest += other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest -= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest -= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest <<= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest <<= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest >>= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest >>= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest &= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest &= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest ^= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest ^= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest |= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest |= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Comma operator.  *******************************************/
+
+void test_comma_operator (int a, int b)
+{
+  __emit_expression_range (0, (a++, a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a++, a + b) );
+                               ~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Literals.  **************************************************/
+
+/* We can't test the ranges of literals directly, since the underlying
+   tree nodes don't retain a location.  However, we can test that they
+   have ranges during parsing by building compound expressions using
+   them, and verifying the ranges of the compound expressions.  */
+
+void test_string_literals (int i)
+{
+  __emit_expression_range (0, "foo"[i] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, "foo"[i] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, &"foo" "bar" ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &"foo" "bar" );
+                               ^~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Examples of non-trivial expressions.  ****************************/
+
+extern double sqrt (double x);
+
+void test_quadratic (double a, double b, double c)
+{
+  __emit_expression_range (0, b * b - 4 * a * c ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, b * b - 4 * a * c );
+                               ~~~~~~^~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0,
+     (-b + sqrt (b * b - 4 * a * c))
+     / (2 * a)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+      (-b + sqrt (b * b - 4 * a * c))
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      / (2 * a));
+      ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* C++-specific expresssions. ****************************************/
+
+void test_cp_literal_keywords (int a, int b)
+{
+  this; /* { dg-error "invalid use of 'this' in non-member function" } */
+/* { dg-begin-multiline-output "" }
+   this;
+   ^~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+class base {
+ public:
+  base ();
+  base (int i);
+  virtual ~base ();
+};
+class derived : public base { ~derived (); };
+
+void test_cp_casts (base *ptr)
+{
+  __emit_expression_range (0, dynamic_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dynamic_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, static_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, static_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, reinterpret_cast <int *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, reinterpret_cast <int *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, const_cast <base *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, const_cast <base *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_new (void)
+{
+  __emit_expression_range (0, ::new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ::new base);
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base);
+                               ^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base));
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base (42));
+                               ^~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base) (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base) (42));
+                               ^~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  /* TODO: placement new.  */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
index 3ed1397..3be89a0 100644
--- a/gcc/testsuite/g++.dg/plugin/plugin.exp
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -62,7 +62,10 @@ set plugin_test_list [list \
     { dumb_plugin.c dumb-plugin-test-1.C } \
     { header_plugin.c header-plugin-test.C } \
     { decl_plugin.c decl-plugin-test.C } \
-    { def_plugin.c def-plugin-test.C } ]
+    { def_plugin.c def-plugin-test.C } \
+    { ../../gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c \
+	  diagnostic-test-expressions-1.C } \
+]
 
 foreach plugin_test $plugin_test_list {
     # Replace each source file with its full-path name
diff --git a/gcc/tree.c b/gcc/tree.c
index d5a71a3..e7f4dcf 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13884,7 +13884,7 @@ nonnull_arg_p (const_tree arg)
 /* Given location LOC, strip away any packed range information
    or ad-hoc information.  */
 
-static location_t
+location_t
 get_pure_location (location_t loc)
 {
   if (IS_ADHOC_LOC (loc))
@@ -13914,20 +13914,20 @@ set_block (location_t loc, tree block)
   return COMBINE_LOCATION_DATA (line_table, pure_loc, src_range, block);
 }
 
-void
+location_t
 set_source_range (tree expr, location_t start, location_t finish)
 {
   source_range src_range;
   src_range.m_start = start;
   src_range.m_finish = finish;
-  set_source_range (expr, src_range);
+  return set_source_range (expr, src_range);
 }
 
-void
+location_t
 set_source_range (tree expr, source_range src_range)
 {
   if (!EXPR_P (expr))
-    return;
+    return UNKNOWN_LOCATION;
 
   location_t pure_loc = get_pure_location (EXPR_LOCATION (expr));
   location_t adhoc = COMBINE_LOCATION_DATA (line_table,
@@ -13935,6 +13935,21 @@ set_source_range (tree expr, source_range src_range)
 					    src_range,
 					    NULL);
   SET_EXPR_LOCATION (expr, adhoc);
+  return adhoc;
+}
+
+location_t
+make_location (location_t caret, location_t start, location_t finish)
+{
+  location_t pure_loc = get_pure_location (caret);
+  source_range src_range;
+  src_range.m_start = start;
+  src_range.m_finish = finish;
+  location_t combined_loc = COMBINE_LOCATION_DATA (line_table,
+						   pure_loc,
+						   src_range,
+						   NULL);
+  return combined_loc;
 }
 
 /* Return the name of combined function FN, for debugging purposes.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 41c0f7c..7919860 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5337,6 +5337,7 @@ type_with_alias_set_p (const_tree t)
   return false;
 }
 
+extern location_t get_pure_location (location_t loc);
 extern location_t set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
@@ -5345,10 +5346,10 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
 
 extern bool nonnull_arg_p (const_tree);
 
-extern void
+extern location_t
 set_source_range (tree expr, location_t start, location_t finish);
 
-extern void
+extern location_t
 set_source_range (tree expr, source_range src_range);
 
 static inline source_range
@@ -5358,4 +5359,7 @@ get_decl_source_range (tree decl)
   return get_range_from_loc (line_table, loc);
 }
 
+extern location_t
+make_location (location_t caret, location_t start, location_t finish);
+
 #endif  /* GCC_TREE_H  */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 1/2] RFC: C++: attempt to provide location_t in more places
  2015-11-25 20:32         ` [PATCH 1/2] RFC: C++: attempt to provide location_t in more places David Malcolm
@ 2015-11-25 21:33           ` Jason Merrill
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
  0 siblings, 1 reply; 40+ messages in thread
From: Jason Merrill @ 2015-11-25 21:33 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

> It's not clear to me whether I should be passing in UNKNOWN_LOCATION
> or input_location to the various functions.
>
> cp_build_unary_op used input_location in various places internally,
> so I've passed that in wherever there isn't a better value.

Rather than try to get this right now I'm inclined to save it for the 
next stage 1 and go back to protected_set_expr_location for GCC 6.

> Bootstraps (on x86_64-pc-linux-gnu), but regresses some tests, due to
> changes in locations at which diagnostics are emitted:
>
>   c-c++-common/cilk-plus/CK/cilk_for_errors.c
>   c-c++-common/cilk-plus/PS/for1.c
>   c-c++-common/gomp/pr59073.c
>   g++.dg/cpp0x/nsdmi-template14.C
>   g++.dg/gomp/for-1.C
>   g++.dg/gomp/pr39495-2.C
>   g++.dg/init/new38.C
>   g++.dg/warn/Wconversion-real-integer2.C
>   g++.dg/warn/pr35635.C

Are the changes good or bad?

Jason

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 04/10] Fix g++.dg/template/crash55.C
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
@ 2015-12-03 14:36               ` David Malcolm
  2015-12-03 14:37               ` [PATCH 10/10] Fix g++.dg/warn/Wconversion-real-integer2.C David Malcolm
                                 ` (8 subsequent siblings)
  9 siblings, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:36 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

The patch kit changes the output of this case:

  1  //PR c++/27668
  2
  3  template<typename class T, T = T()> // { dg-error "nested-name-specifier|two or more|valid type" }
  4  struct A {};
  5
  6  template<int> void foo(A<int>);  // { dg-error "cast|argument" "" { target c++98_only } }

but only for c++98, from:
  g++.dg/template/crash55.C:3:19: error: expected nested-name-specifier before 'class'
  g++.dg/template/crash55.C:3:25: error: two or more data types in declaration of 'parameter'
  g++.dg/template/crash55.C:3:34: error: 'class T' is not a valid type for a template non-type parameter
  g++.dg/template/crash55.C:6:29: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
  g++.dg/template/crash55.C:6:29: error: template argument 2 is invalid
to:
  g++.dg/template/crash55.C:3:19: error: expected nested-name-specifier before 'class'
  g++.dg/template/crash55.C:3:25: error: two or more data types in declaration of 'parameter'
  g++.dg/template/crash55.C:3:34: error: 'class T' is not a valid type for a template non-type parameter
  g++.dg/template/crash55.C:3:32: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
  g++.dg/template/crash55.C:6:29: error: template argument 2 is invalid

i.e. the 4th error moves from line 6 to line 3
("a cast to a type other than an integral or enumeration type cannot appear in a constant-expression")

This change is reasonable, so the patch updates the dg-error
directives accordingly.

gcc/testsuite/ChangeLog:
	* g++.dg/template/crash55.C: Update dg-error directives.
---
 gcc/testsuite/g++.dg/template/crash55.C | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/gcc/testsuite/g++.dg/template/crash55.C b/gcc/testsuite/g++.dg/template/crash55.C
index 9b80fd1..b9b29f7 100644
--- a/gcc/testsuite/g++.dg/template/crash55.C
+++ b/gcc/testsuite/g++.dg/template/crash55.C
@@ -1,6 +1,7 @@
 //PR c++/27668
 
 template<typename class T, T = T()> // { dg-error "nested-name-specifier|two or more|valid type" }
+// { dg-error "cast" "" { target c++98_only } 3 }
 struct A {};
 
-template<int> void foo(A<int>);	// { dg-error "cast|argument" "" { target c++98_only } }
+template<int> void foo(A<int>);	// { dg-error "template argument 2" "" { target c++98_only } }
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 00/10] C++ expression ranges v4
  2015-11-25 21:33           ` Jason Merrill
@ 2015-12-03 14:36             ` David Malcolm
  2015-12-03 14:36               ` [PATCH 04/10] Fix g++.dg/template/crash55.C David Malcolm
                                 ` (9 more replies)
  0 siblings, 10 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:36 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

On Wed, 2015-11-25 at 16:26 -0500, Jason Merrill wrote:
> > It's not clear to me whether I should be passing in UNKNOWN_LOCATION
> > or input_location to the various functions.
> >
> > cp_build_unary_op used input_location in various places internally,
> > so I've passed that in wherever there isn't a better value.
> 
> Rather than try to get this right now I'm inclined to save it for the 
> next stage 1 and go back to protected_set_expr_location for GCC 6.

Thanks; I've reworked the patch based on that idea.  I found whilst
bugfixing that in general it was better to use
cp_expr::set_location, which calls protected_set_expr_location,
since the former sets both the location in the tree node (if any)
*and* the shadow copy in the cp_expr (thus ensuring that compound
expressions use the correct location_t).

I've also done a lot of bugfixing, and rebased
from r230562 (Nov 18th) to r231208 (Dec 2nd).

> > Bootstraps (on x86_64-pc-linux-gnu), but regresses some tests, due to
> > changes in locations at which diagnostics are emitted:
> >
> >   c-c++-common/cilk-plus/CK/cilk_for_errors.c
> >   c-c++-common/cilk-plus/PS/for1.c
> >   c-c++-common/gomp/pr59073.c
> >   g++.dg/cpp0x/nsdmi-template14.C
> >   g++.dg/gomp/for-1.C
> >   g++.dg/gomp/pr39495-2.C
> >   g++.dg/init/new38.C
> >   g++.dg/warn/Wconversion-real-integer2.C
> >   g++.dg/warn/pr35635.C
> 
> Are the changes good or bad?

Some were bad, which I've fixed in the code.  Others were
improvements, requiring tweaks/movement of dg- directives.
I've broken out any such changes I needed to make to
specific test cases as separate patches in the kit, with notes
on each, in the hope it will make review easier.  (The kit would be
applied as a single commit; I've been testing it as one).

The following 10-patch kit bootstraps&regrtests successfully on
x86_64-pc-linux-gnu.

It adds 213 new PASS results to g++.sum, and changes the location
of 154 PASS results there.

It adds 16 new PASS results to obj-c++.sum.

OK for trunk for gcc 6?


David Malcolm (10):
  C++ FE: expression ranges v4
  Fix g++.dg/cpp0x/nsdmi-template14.C
  Fix g++.dg/gomp/loop-1.C
  Fix g++.dg/template/crash55.C
  Fix location of dg-error within g++.dg/template/pr64100.C
  Fix g++.dg/template/pseudodtor3.C
  Fix g++.dg/template/ref3.C
  Fix g++.dg/ubsan/pr63956.C
  Fix g++.dg/warn/pr35635.C
  Fix g++.dg/warn/Wconversion-real-integer2.C

 gcc/convert.c                                      |   9 +-
 gcc/cp/cp-tree.h                                   |  86 ++-
 gcc/cp/cvt.c                                       |   4 +-
 gcc/cp/name-lookup.c                               |   6 +-
 gcc/cp/name-lookup.h                               |   2 +-
 gcc/cp/parser.c                                    | 576 +++++++++++----
 gcc/cp/semantics.c                                 |  53 +-
 gcc/cp/typeck.c                                    |  42 +-
 gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C      |   4 +-
 gcc/testsuite/g++.dg/gomp/loop-1.C                 |  32 +-
 .../g++.dg/plugin/diagnostic-test-expressions-1.C  | 775 +++++++++++++++++++++
 gcc/testsuite/g++.dg/plugin/plugin.exp             |   5 +-
 gcc/testsuite/g++.dg/template/crash55.C            |   3 +-
 gcc/testsuite/g++.dg/template/pr64100.C            |   4 +-
 gcc/testsuite/g++.dg/template/pseudodtor3.C        |   4 +-
 gcc/testsuite/g++.dg/template/ref3.C               |   6 +-
 gcc/testsuite/g++.dg/ubsan/pr63956.C               |  28 +-
 .../g++.dg/warn/Wconversion-real-integer2.C        |   4 +-
 gcc/testsuite/g++.dg/warn/pr35635.C                |   6 +-
 .../plugin/diagnostic-test-expressions-1.mm        |  94 +++
 gcc/testsuite/obj-c++.dg/plugin/plugin.exp         |  90 +++
 gcc/tree.c                                         |  25 +-
 gcc/tree.h                                         |  17 +-
 23 files changed, 1632 insertions(+), 243 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
 create mode 100644 gcc/testsuite/obj-c++.dg/plugin/diagnostic-test-expressions-1.mm
 create mode 100644 gcc/testsuite/obj-c++.dg/plugin/plugin.exp

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 03/10] Fix g++.dg/gomp/loop-1.C
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
                                 ` (4 preceding siblings ...)
  2015-12-03 14:37               ` [PATCH 02/10] Fix g++.dg/cpp0x/nsdmi-template14.C David Malcolm
@ 2015-12-03 14:37               ` David Malcolm
  2015-12-03 14:37               ` [PATCH 08/10] Fix g++.dg/ubsan/pr63956.C David Malcolm
                                 ` (3 subsequent siblings)
  9 siblings, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:37 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

The patch kit affects the locations of the errors reported by
g++.dg/gomp/loop-1.C.

I reviewed the new locations, and they seemed sane.

This patch updates the locations of omp_for_cond to use the location of
the cond if available, falling back to the existing behavior of using
input_location otherwise.  This improves the reported locations.

The patch also updates the testcase to reflect the various changes
to the locations.

For reference, here's the updated output from the testcase (with
caret-printing enabled):

g++.dg/gomp/loop-1.C: In function ‘void f1(int)’:
g++.dg/gomp/loop-1.C:21:3: error: initializer expression refers to iteration variable ‘i’
   for (i = i; i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:24:14: error: initializer expression refers to iteration variable ‘i’
   for (i = 2 * (i & x); i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
            ~~^~~~~~~~~

g++.dg/gomp/loop-1.C:27:3: error: initializer expression refers to iteration variable ‘i’
   for (i = bar (i); i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:30:3: error: initializer expression refers to iteration variable ‘i’
   for (i = baz (&i); i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:33:17: error: condition expression refers to iteration variable ‘i’
   for (i = 5; i < 2 * i + 17; i++) /* { dg-error "condition expression refers to iteration variable" } */
               ~~^~~~~~~~~~~~

g++.dg/gomp/loop-1.C:36:26: error: condition expression refers to iteration variable ‘i’
   for (i = 5; 2 * i + 17 > i; i++) /* { dg-error "condition expression refers to iteration variable" } */
               ~~~~~~~~~~~^~~

g++.dg/gomp/loop-1.C:39:23: error: condition expression refers to iteration variable ‘i’
   for (i = 5; bar (i) > i; i++) /* { dg-error "condition expression refers to iteration variable" } */
               ~~~~~~~~^~~

g++.dg/gomp/loop-1.C:42:17: error: condition expression refers to iteration variable ‘i’
   for (i = 5; i <= baz (&i); i++) /* { dg-error "condition expression refers to iteration variable" } */
               ~~^~~~~~~~~~~

g++.dg/gomp/loop-1.C:45:17: error: condition expression refers to iteration variable ‘i’
   for (i = 5; i <= i; i++) /* { dg-error "invalid controlling predicate|condition expression refers to iteration variable" } */
               ~~^~~~

g++.dg/gomp/loop-1.C:48:3: error: increment expression refers to iteration variable ‘i’
   for (i = 5; i < 16; i += i) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:51:33: error: increment expression refers to iteration variable ‘i’
   for (i = 5; i < 16; i = i + 2 * i) /* { dg-error "invalid increment expression|increment expression refers to iteration variable" } */
                               ~~^~~

g++.dg/gomp/loop-1.C:54:3: error: increment expression refers to iteration variable ‘i’
   for (i = 5; i < 16; i = i + i) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:57:35: error: increment expression refers to iteration variable ‘i’
   for (i = 5; i < 16; i = i + bar (i)) /* { dg-error "increment expression refers to iteration variable" } */
                               ~~~~^~~

g++.dg/gomp/loop-1.C:60:31: error: increment expression refers to iteration variable ‘i’
   for (i = 5; i < 16; i = baz (&i) + i) /* { dg-error "increment expression refers to iteration variable" } */
                           ~~~~^~~~

g++.dg/gomp/loop-1.C:63:32: error: increment expression refers to iteration variable ‘i’
   for (i = 5; i < 16; i += bar (i)) /* { dg-error "increment expression refers to iteration variable" } */
                            ~~~~^~~

g++.dg/gomp/loop-1.C:66:32: error: increment expression refers to iteration variable ‘i’
   for (i = 5; i < 16; i += baz (&i)) /* { dg-error "increment expression refers to iteration variable" } */
                            ~~~~^~~~

g++.dg/gomp/loop-1.C:73:3: error: initializer expression refers to iteration variable ‘j’
   for (i = j; i < 16; i = i + 2) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:77:3: error: initializer expression refers to iteration variable ‘i’
   for (i = 0; i < 16; i = i + 2) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:82:16: error: initializer expression refers to iteration variable ‘i’
     for (j = i + 3; j < 16; j += 2) /* { dg-error "initializer expression refers to iteration variable" } */
              ~~^~~

g++.dg/gomp/loop-1.C:85:3: error: initializer expression refers to iteration variable ‘i’
   for (i = 0; i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:90:20: error: condition expression refers to iteration variable ‘i’
     for (j = 16; j > (i & x); j--) /* { dg-error "condition expression refers to iteration variable" } */
                  ~~^~~~~~~~~

g++.dg/gomp/loop-1.C:94:19: error: condition expression refers to iteration variable ‘i’
     for (j = 0; j < i; j++) /* { dg-error "condition expression refers to iteration variable" } */
                 ~~^~~

g++.dg/gomp/loop-1.C:98:19: error: condition expression refers to iteration variable ‘i’
     for (j = 0; j < i + 4; j++) /* { dg-error "condition expression refers to iteration variable" } */
                 ~~^~~~~~~

g++.dg/gomp/loop-1.C:101:17: error: condition expression refers to iteration variable ‘j’
   for (i = 0; i < j + 4; i++) /* { dg-error "condition expression refers to iteration variable" } */
               ~~^~~~~~~

g++.dg/gomp/loop-1.C:105:17: error: condition expression refers to iteration variable ‘j’
   for (i = 0; i < j; i++) /* { dg-error "condition expression refers to iteration variable" } */
               ~~^~~

g++.dg/gomp/loop-1.C:109:17: error: condition expression refers to iteration variable ‘j’
   for (i = 0; i < bar (j); i++) /* { dg-error "condition expression refers to iteration variable" } */
               ~~^~~~~~~~~

g++.dg/gomp/loop-1.C:114:19: error: condition expression refers to iteration variable ‘i’
     for (j = 0; j < baz (&i); j++) /* { dg-error "condition expression refers to iteration variable" } */
                 ~~^~~~~~~~~~

g++.dg/gomp/loop-1.C:117:3: error: increment expression refers to iteration variable ‘j’
   for (i = 0; i < 16; i += j) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:121:3: error: increment expression refers to iteration variable ‘i’
   for (i = 0; i < 16; i++) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:125:3: error: increment expression refers to iteration variable ‘j’
   for (i = 0; i < 16; i = j + i) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:129:3: error: increment expression refers to iteration variable ‘i’
   for (i = 0; i < 16; i++) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:133:31: error: increment expression refers to iteration variable ‘j’
   for (i = 0; i < 16; i = bar (j) + i) /* { dg-error "increment expression refers to iteration variable" } */
                           ~~~~^~~

g++.dg/gomp/loop-1.C:138:37: error: increment expression refers to iteration variable ‘i’
     for (j = 0; j < 16; j = j + baz (&i)) /* { dg-error "increment expression refers to iteration variable" } */
                                 ~~~~^~~~

g++.dg/gomp/loop-1.C: In function ‘void f2(int)’:
g++.dg/gomp/loop-1.C:158:3: error: initializer expression refers to iteration variable ‘i’
   for (int i = i; i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:161:18: error: initializer expression refers to iteration variable ‘i’
   for (int i = 2 * (i & x); i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
                ~~^~~~~~~~~

g++.dg/gomp/loop-1.C:164:3: error: initializer expression refers to iteration variable ‘i’
   for (int i = bar (i); i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:167:3: error: initializer expression refers to iteration variable ‘i’
   for (int i = baz (&i); i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:170:21: error: condition expression refers to iteration variable ‘i’
   for (int i = 5; i < 2 * i + 17; i++) /* { dg-error "condition expression refers to iteration variable" } */
                   ~~^~~~~~~~~~~~

g++.dg/gomp/loop-1.C:173:30: error: condition expression refers to iteration variable ‘i’
   for (int i = 5; 2 * i + 17 > i; i++) /* { dg-error "condition expression refers to iteration variable" } */
                   ~~~~~~~~~~~^~~

g++.dg/gomp/loop-1.C:176:27: error: condition expression refers to iteration variable ‘i’
   for (int i = 5; bar (i) > i; i++) /* { dg-error "condition expression refers to iteration variable" } */
                   ~~~~~~~~^~~

g++.dg/gomp/loop-1.C:179:21: error: condition expression refers to iteration variable ‘i’
   for (int i = 5; i <= baz (&i); i++) /* { dg-error "condition expression refers to iteration variable" } */
                   ~~^~~~~~~~~~~

g++.dg/gomp/loop-1.C:182:21: error: condition expression refers to iteration variable ‘i’
   for (int i = 5; i <= i; i++) /* { dg-error "invalid controlling predicate|condition expression refers to iteration variable" } */
                   ~~^~~~

g++.dg/gomp/loop-1.C:185:3: error: increment expression refers to iteration variable ‘i’
   for (int i = 5; i < 16; i += i) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:188:37: error: increment expression refers to iteration variable ‘i’
   for (int i = 5; i < 16; i = i + 2 * i) /* { dg-error "invalid increment expression|increment expression refers to iteration variable" } */
                                   ~~^~~

g++.dg/gomp/loop-1.C:191:3: error: increment expression refers to iteration variable ‘i’
   for (int i = 5; i < 16; i = i + i) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:194:39: error: increment expression refers to iteration variable ‘i’
   for (int i = 5; i < 16; i = i + bar (i)) /* { dg-error "increment expression refers to iteration variable" } */
                                   ~~~~^~~

g++.dg/gomp/loop-1.C:197:35: error: increment expression refers to iteration variable ‘i’
   for (int i = 5; i < 16; i = baz (&i) + i) /* { dg-error "increment expression refers to iteration variable" } */
                               ~~~~^~~~

g++.dg/gomp/loop-1.C:200:36: error: increment expression refers to iteration variable ‘i’
   for (int i = 5; i < 16; i += bar (i)) /* { dg-error "increment expression refers to iteration variable" } */
                                ~~~~^~~

g++.dg/gomp/loop-1.C:203:36: error: increment expression refers to iteration variable ‘i’
   for (int i = 5; i < 16; i += baz (&i)) /* { dg-error "increment expression refers to iteration variable" } */
                                ~~~~^~~~

g++.dg/gomp/loop-1.C:210:3: error: initializer expression refers to iteration variable ‘i’
   for (int i = 0; i < 16; i = i + 2) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:215:20: error: initializer expression refers to iteration variable ‘i’
     for (int j = i + 3; j < 16; j += 2) /* { dg-error "initializer expression refers to iteration variable" } */
                  ~~^~~

g++.dg/gomp/loop-1.C:218:3: error: initializer expression refers to iteration variable ‘i’
   for (int i = 0; i < 16; i++) /* { dg-error "initializer expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:223:24: error: condition expression refers to iteration variable ‘i’
     for (int j = 16; j > (i & x); j--) /* { dg-error "condition expression refers to iteration variable" } */
                      ~~^~~~~~~~~

g++.dg/gomp/loop-1.C:227:23: error: condition expression refers to iteration variable ‘i’
     for (int j = 0; j < i; j++) /* { dg-error "condition expression refers to iteration variable" } */
                     ~~^~~

g++.dg/gomp/loop-1.C:231:23: error: condition expression refers to iteration variable ‘i’
     for (int j = 0; j < i + 4; j++) /* { dg-error "condition expression refers to iteration variable" } */
                     ~~^~~~~~~

g++.dg/gomp/loop-1.C:235:23: error: condition expression refers to iteration variable ‘i’
     for (int j = 0; j < baz (&i); j++) /* { dg-error "condition expression refers to iteration variable" } */
                     ~~^~~~~~~~~~

g++.dg/gomp/loop-1.C:238:3: error: increment expression refers to iteration variable ‘i’
   for (int i = 0; i < 16; i++) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:242:3: error: increment expression refers to iteration variable ‘i’
   for (int i = 0; i < 16; i++) /* { dg-error "increment expression refers to iteration variable" } */
   ^~~

g++.dg/gomp/loop-1.C:247:41: error: increment expression refers to iteration variable ‘i’
     for (int j = 0; j < 16; j = j + baz (&i)) /* { dg-error "increment expression refers to iteration variable" } */
                                     ~~~~^~~~

gcc/cp/ChangeLog:
	* parser.c (cp_parser_omp_for_cond): Attempt to use the location
	of "cond" for the binary op.

gcc/testsuite/ChangeLog:
	* g++.dg/gomp/loop-1.C: Update dg-error locations.
---
 gcc/cp/parser.c                    |  3 ++-
 gcc/testsuite/g++.dg/gomp/loop-1.C | 32 ++++++++++++++++----------------
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index f3d406e..284dadd0 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -32843,7 +32843,8 @@ cp_parser_omp_for_cond (cp_parser *parser, tree decl, enum tree_code code)
 	  || CLASS_TYPE_P (TREE_TYPE (decl))))
     return cond;
 
-  return build_x_binary_op (input_location, TREE_CODE (cond),
+  return build_x_binary_op (EXPR_LOC_OR_LOC (cond, input_location),
+			    TREE_CODE (cond),
 			    TREE_OPERAND (cond, 0), ERROR_MARK,
 			    TREE_OPERAND (cond, 1), ERROR_MARK,
 			    /*overload=*/NULL, tf_warning_or_error);
diff --git a/gcc/testsuite/g++.dg/gomp/loop-1.C b/gcc/testsuite/g++.dg/gomp/loop-1.C
index 46e707f..de08eb3 100644
--- a/gcc/testsuite/g++.dg/gomp/loop-1.C
+++ b/gcc/testsuite/g++.dg/gomp/loop-1.C
@@ -86,16 +86,16 @@ f1 (int x)
     for (j = baz (&i); j < 16; j += 2)
       ;
   #pragma omp for collapse(2)
-  for (i = 0; i < 16; i++) /* { dg-error "condition expression refers to iteration variable" } */
-    for (j = 16; j > (i & x); j--)
+  for (i = 0; i < 16; i++)
+    for (j = 16; j > (i & x); j--) /* { dg-error "condition expression refers to iteration variable" } */
       ;
   #pragma omp for collapse(2)
-  for (i = 0; i < 16; i++) /* { dg-error "condition expression refers to iteration variable" } */
-    for (j = 0; j < i; j++)
+  for (i = 0; i < 16; i++)
+    for (j = 0; j < i; j++) /* { dg-error "condition expression refers to iteration variable" } */
       ;
   #pragma omp for collapse(2)
-  for (i = 0; i < 16; i++) /* { dg-error "condition expression refers to iteration variable" } */
-    for (j = 0; j < i + 4; j++)
+  for (i = 0; i < 16; i++)
+    for (j = 0; j < i + 4; j++) /* { dg-error "condition expression refers to iteration variable" } */
       ;
   #pragma omp for collapse(2)
   for (i = 0; i < j + 4; i++) /* { dg-error "condition expression refers to iteration variable" } */
@@ -110,8 +110,8 @@ f1 (int x)
     for (j = 0; j < 16; j++)
       ;
   #pragma omp for collapse(2)
-  for (i = 0; i < 16; i++) /* { dg-error "condition expression refers to iteration variable" } */
-    for (j = 0; j < baz (&i); j++)
+  for (i = 0; i < 16; i++)
+    for (j = 0; j < baz (&i); j++) /* { dg-error "condition expression refers to iteration variable" } */
       ;
   #pragma omp for collapse(2)
   for (i = 0; i < 16; i += j) /* { dg-error "increment expression refers to iteration variable" } */
@@ -219,20 +219,20 @@ f2 (int x)
     for (int j = baz (&i); j < 16; j += 2)
       ;
   #pragma omp for collapse(2)
-  for (int i = 0; i < 16; i++) /* { dg-error "condition expression refers to iteration variable" } */
-    for (int j = 16; j > (i & x); j--)
+  for (int i = 0; i < 16; i++)
+    for (int j = 16; j > (i & x); j--) /* { dg-error "condition expression refers to iteration variable" } */
       ;
   #pragma omp for collapse(2)
-  for (int i = 0; i < 16; i++) /* { dg-error "condition expression refers to iteration variable" } */
-    for (int j = 0; j < i; j++)
+  for (int i = 0; i < 16; i++)
+    for (int j = 0; j < i; j++) /* { dg-error "condition expression refers to iteration variable" } */
       ;
   #pragma omp for collapse(2)
-  for (int i = 0; i < 16; i++) /* { dg-error "condition expression refers to iteration variable" } */
-    for (int j = 0; j < i + 4; j++)
+  for (int i = 0; i < 16; i++)
+    for (int j = 0; j < i + 4; j++) /* { dg-error "condition expression refers to iteration variable" } */
       ;
   #pragma omp for collapse(2)
-  for (int i = 0; i < 16; i++) /* { dg-error "condition expression refers to iteration variable" } */
-    for (int j = 0; j < baz (&i); j++)
+  for (int i = 0; i < 16; i++)
+    for (int j = 0; j < baz (&i); j++) /* { dg-error "condition expression refers to iteration variable" } */
       ;
   #pragma omp for collapse(2)
   for (int i = 0; i < 16; i++) /* { dg-error "increment expression refers to iteration variable" } */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 02/10] Fix g++.dg/cpp0x/nsdmi-template14.C
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
                                 ` (3 preceding siblings ...)
  2015-12-03 14:37               ` [PATCH 07/10] Fix g++.dg/template/ref3.C David Malcolm
@ 2015-12-03 14:37               ` David Malcolm
  2015-12-03 20:33                 ` Jason Merrill
  2015-12-03 14:37               ` [PATCH 03/10] Fix g++.dg/gomp/loop-1.C David Malcolm
                                 ` (4 subsequent siblings)
  9 siblings, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:37 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

When building new-expressions, we use cp_lexer_previous_token
and access its location to get the final position in the source
range.

Within g++.dg/cpp0x/nsdmi-template14.C, the previous token
within a new expr can have been purged, leading to UNKNOWN_LOCATION.

  g++.dg/cpp0x/nsdmi-template14.C:11:10: error: recursive instantiation of non-static data member initializer for ‘B<1>::p’
  B* p = new B<N>;

(note the lack of caret)

(gdb) p *end_tok
$54 = {type = CPP_GREATER, keyword = RID_MAX, flags = 0 '\000', pragma_kind = PRAGMA_NONE, implicit_extern_c = 0,
error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value = 0x0, value = <tree 0x0>}}

This patch adds bulletproofing to detect purged tokens, and avoid using
them.

Alternatively, is it OK to access purged tokens for this kind of thing?
If so, would it make more sense to instead leave their locations untouched
when purging them?

The patch also updates the location of a dg-error directive in the
testcase to reflect improved location information.

gcc/cp/ChangeLog:
	* parser.c (cp_parser_new_expression): Avoid accessing purged
	tokens when getting end of location range.

gcc/testsuite/ChangeLog:
	* g++.dg/cpp0x/nsdmi-template14.C: Move dg-error directive.
---
 gcc/cp/parser.c                               | 10 +++++++---
 gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C |  4 ++--
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index d859a89..f3d406e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -7957,9 +7957,13 @@ cp_parser_new_expression (cp_parser* parser)
          with caret == start at the start of the "new" token, and the end
          at the end of the final token we consumed.  */
       cp_token *end_tok = cp_lexer_previous_token (parser->lexer);
-      location_t end_loc = get_finish (end_tok->location);
-      location_t combined_loc = make_location (start_loc, start_loc, end_loc);
-
+      location_t combined_loc = start_loc;
+      if (!end_tok->purged_p)
+        {
+          location_t end_loc = get_finish (end_tok->location);
+          gcc_assert (end_loc);
+          combined_loc = make_location (start_loc, start_loc, end_loc);
+        }
       /* Create a representation of the new-expression.  */
       ret = build_new (&placement, type, nelts, &initializer, global_scope_p,
 		       tf_warning_or_error);
diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C
index 9cb01f1..47f5b63 100644
--- a/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C
+++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C
@@ -8,10 +8,10 @@ template<int> struct A // { dg-error "has been parsed" }
 
 template<int N> struct B
 {
-  B* p = new B<N>;
+  B* p = new B<N>; // { dg-error "recursive instantiation of non-static data" }
 };
 
-B<1> x; // { dg-error "recursive instantiation of non-static data" }
+B<1> x;
 
 struct C
 {
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 10/10] Fix g++.dg/warn/Wconversion-real-integer2.C
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
  2015-12-03 14:36               ` [PATCH 04/10] Fix g++.dg/template/crash55.C David Malcolm
@ 2015-12-03 14:37               ` David Malcolm
  2015-12-03 14:37               ` [PATCH 01/10] C++ FE: expression ranges v4 David Malcolm
                                 ` (7 subsequent siblings)
  9 siblings, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:37 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

This testcase's output is changed by the patchkit from printing at the "=":

BEFORE:
g++.dg/warn/Wconversion-real-integer2.C: In function 'void h()':
g++.dg/warn/Wconversion-real-integer2.C:32:12: warning: conversion to 'float' alters 'int' constant value [-Wfloat-conversion]
     vfloat = INT_MAX; // { dg-warning "conversion to .float. alters .int. constant value" }
            ^
to showing the token of interest and its macro expansion:

AFTER:
g++.dg/warn/Wconversion-real-integer2.C: In function ‘void h()’:
g++.dg/warn/Wconversion-real-integer2.C:26:17: warning: conversion to ‘float’ alters ‘int’ constant value [-Wfloat-conversion]
 #define INT_MAX __INT_MAX__
                 ^

g++.dg/warn/Wconversion-real-integer2.C:32:14: note: in expansion of macro ‘INT_MAX’
     vfloat = INT_MAX; // { dg-warning "conversion to .float. alters .int. constant value" }
              ^~~~~~~

This is an improvement, so this patch updates the test case accordingly.

gcc/testsuite/ChangeLog:
	* g++.dg/warn/Wconversion-real-integer2.C: Update location of
	dg-warning; add a dg-message.
---
 gcc/testsuite/g++.dg/warn/Wconversion-real-integer2.C | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gcc/testsuite/g++.dg/warn/Wconversion-real-integer2.C b/gcc/testsuite/g++.dg/warn/Wconversion-real-integer2.C
index 0494588..7e39d5f 100644
--- a/gcc/testsuite/g++.dg/warn/Wconversion-real-integer2.C
+++ b/gcc/testsuite/g++.dg/warn/Wconversion-real-integer2.C
@@ -23,11 +23,11 @@
 //
 // That is more useful.
 
-#define INT_MAX __INT_MAX__ 
+#define INT_MAX __INT_MAX__ // { dg-warning "17: conversion to .float. alters .int. constant value" }
 
 float  vfloat;
 
 void h (void)
 {
-    vfloat = INT_MAX; // { dg-warning "conversion to .float. alters .int. constant value" }
+    vfloat = INT_MAX; // { dg-message "14: in expansion of macro .INT_MAX." }
 }
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 05/10] Fix location of dg-error within g++.dg/template/pr64100.C
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
                                 ` (6 preceding siblings ...)
  2015-12-03 14:37               ` [PATCH 08/10] Fix g++.dg/ubsan/pr63956.C David Malcolm
@ 2015-12-03 14:37               ` David Malcolm
  2015-12-03 14:37               ` [PATCH 09/10] Fix g++.dg/warn/pr35635.C David Malcolm
  2015-12-03 14:53               ` [PATCH 06/10] Fix g++.dg/template/pseudodtor3.C David Malcolm
  9 siblings, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:37 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

Here's what it now emits (if caret-printing were enabled):

g++.dg/template/pr64100.C: In instantiation of ‘class foo<int>’:
g++.dg/template/pr64100.C:8:16:   required from here
g++.dg/template/pr64100.C:5:41: error: invalid use of incomplete type ‘class foo<int>’
     static_assert(noexcept(((foo *)1)->~foo()), ""); // { dg-error "incomplete type" }
                            ~~~~~~~~~~~~~^~~

g++.dg/template/pr64100.C:3:27: note: definition of ‘class foo<int>’ is not complete until the closing brace
 template<typename> struct foo // { dg-message "note" }
                           ^~~

gcc/testsuite/ChangeLog:
	* g++.dg/template/pr64100.C: Update location of dg-error
	directive.
---
 gcc/testsuite/g++.dg/template/pr64100.C | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gcc/testsuite/g++.dg/template/pr64100.C b/gcc/testsuite/g++.dg/template/pr64100.C
index 493849f..051800c 100644
--- a/gcc/testsuite/g++.dg/template/pr64100.C
+++ b/gcc/testsuite/g++.dg/template/pr64100.C
@@ -1,8 +1,8 @@
 // { dg-do compile { target c++11 } }
 
 template<typename> struct foo // { dg-message "note" }
-{ // { dg-error "incomplete type" }
-    static_assert(noexcept(((foo *)1)->~foo()), "");
+{
+    static_assert(noexcept(((foo *)1)->~foo()), ""); // { dg-error "incomplete type" }
 }; 
 
 template class foo<int>;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 09/10] Fix g++.dg/warn/pr35635.C
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
                                 ` (7 preceding siblings ...)
  2015-12-03 14:37               ` [PATCH 05/10] Fix location of dg-error within g++.dg/template/pr64100.C David Malcolm
@ 2015-12-03 14:37               ` David Malcolm
  2015-12-03 14:53               ` [PATCH 06/10] Fix g++.dg/template/pseudodtor3.C David Malcolm
  9 siblings, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:37 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

This testcase was broken by the patch kit; upon investigation
the best fix is to try to use the location of the relevant
expression when warning about conversions, rather than
input_location, falling back to the latter via EXPR_LOC_OR_LOC.

One dg-warning needed moving, since the caret is on the "?" of the
conditional here:

   uchar_x = bar != 0
             ~~~~~~~~
     ? (unsigned char) 1024
     ^~~~~~~~~~~~~~~~~~~~~~
     : -1;
     ~~~~

gcc/cp/ChangeLog:
	* cvt.c (cp_convert_and_check): When warning about conversions,
	attempt to use the location of "expr" if available, otherwise
	falling back to the old behavior of using input_location.

gcc/testsuite/ChangeLog:
	* g++.dg/warn/pr35635.C (func3): Update location of a
	dg-warning.
---
 gcc/cp/cvt.c                        | 4 ++--
 gcc/testsuite/g++.dg/warn/pr35635.C | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index ebca004..f24f280 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -650,8 +650,8 @@ cp_convert_and_check (tree type, tree expr, tsubst_flags_t complain)
       folded_result = fold_simple (folded_result);
       if (!TREE_OVERFLOW_P (folded)
 	  && folded_result != error_mark_node)
-	warnings_for_convert_and_check (input_location, type, folded,
-					folded_result);
+	warnings_for_convert_and_check (EXPR_LOC_OR_LOC (expr, input_location),
+					type, folded, folded_result);
     }
 
   return result;
diff --git a/gcc/testsuite/g++.dg/warn/pr35635.C b/gcc/testsuite/g++.dg/warn/pr35635.C
index de68ceb..19345c5 100644
--- a/gcc/testsuite/g++.dg/warn/pr35635.C
+++ b/gcc/testsuite/g++.dg/warn/pr35635.C
@@ -62,9 +62,9 @@ void func3()
   /* At least one branch of ? does not fit in the destination, thus
      warn.  */
   uchar_x = bar != 0 ? 2.1 : 10; /* { dg-warning "conversion" } */
-  uchar_x = bar != 0  /* { dg-warning "negative integer implicitly converted to unsigned type" } */
-    ? (unsigned char) 1024 
-    : -1; 
+  uchar_x = bar != 0
+    ? (unsigned char) 1024 /* { dg-warning "negative integer implicitly converted to unsigned type" } */
+    : -1;
 }
 
 void func4()
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 01/10] C++ FE: expression ranges v4
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
  2015-12-03 14:36               ` [PATCH 04/10] Fix g++.dg/template/crash55.C David Malcolm
  2015-12-03 14:37               ` [PATCH 10/10] Fix g++.dg/warn/Wconversion-real-integer2.C David Malcolm
@ 2015-12-03 14:37               ` David Malcolm
  2015-12-04 17:10                 ` Jason Merrill
  2015-12-03 14:37               ` [PATCH 07/10] Fix g++.dg/template/ref3.C David Malcolm
                                 ` (6 subsequent siblings)
  9 siblings, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:37 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

Changes in this version:
- removal of gcc_assert (m_loc != UNKNOWN_LOCATION) from cp_expr ctor
- uses protected_set_expr_location or cp_expr::set_location/set_range,
  rather than attempting to add location_t arguments
- adds location support and test coverage based on issues seen in
  the analogous work onthe C FE (see r230497 and r230775).
  Specifically:
  - various Objective C++ constructs (creating obj-c++.dg/plugin in
    order to unit-test these, analogous to changes for C FE)
  - braced initializers
  - statement expressions
  - address of label
  - transaction expressions
  - __FUNCTION__ et al
  - __builtin_va_arg and __builtin_offsetof
- handle locations of functional casts and _Cilk_spawn
- fixes locations of negative numeric literals
- various other bugfixes and additional test coverage

gcc/ChangeLog:
	* convert.c (convert_to_real_1): When converting from a
	REAL_TYPE, preserve the location of EXPR in the result.
	* tree.c (get_pure_location): Make non-static.
	(set_source_range): Return the resulting location_t.
	(make_location): New function.
	* tree.h (get_pure_location): New decl.
	(get_finish): New inline function.
	(set_source_range): Convert return type from void to location_t.
	(make_location): New decl.

gcc/cp/ChangeLog:
	* cp-tree.h (class cp_expr): New class.
	(finish_parenthesized_expr): Convert return type and param to
	cp_expr.
	(perform_koenig_lookup): Convert return type and param from tree
	to cp_expr.
	(finish_increment_expr): Likewise.
	(finish_unary_op_expr): Likewise.
	(finish_id_expression): Likewise for return type.
	(build_class_member_access_expr): Likewise for param.
	(finish_class_member_access_expr): Likewise.
	(build_x_unary_op): Likewise.
	(build_c_cast): New decl.
	(build_x_modify_expr): Convert return type from tree to cp_expr.
	* name-lookup.c (lookup_arg_dependent_1): Likewise.
	(lookup_arg_dependent): Likewise; also for local "ret".
	* name-lookup.h (lookup_arg_dependent): Likewise for return type.
	* parser.c (struct cp_parser_expression_stack_entry): Likewise
	for field "lhs".
	(cp_parser_identifier): Likewise for return type.  Use cp_expr
	ctor to preserve the token's location.
	(cp_parser_string_literal): Likewise, building up a meaningful
	location for the case where a compound string literal is built by
	concatentation.
	(cp_parser_userdef_char_literal): Likewise for return type.
	(cp_parser_userdef_numeric_literal): Likewise.
	(cp_parser_statement_expr): Convert return type to cp_expr.
	Generate a suitable location for the expr and return it via the
	cp_expr ctor.
	(cp_parser_fold_expression): Convert return type to cp_expr.
	(cp_parser_primary_expression): Likewise, and for locals "expr",
	"lam", "id_expression", "decl".
	Use cp_expr ctor when parsing literals, to preserve the spelling
	location of the token.  Preserve the locations of parentheses.
	Preserve location when calling objc_lookup_ivar.
	Preserve the location for "this" tokens.  Generate suitable
	locations for "__builtin_va_arg" constructs and for
	Objective C 2.0 dot-syntax.  Set the location for the result of
	finish_id_expression.
	(cp_parser_primary_expression): Convert return type from tree to
	cp_expr.
	(cp_parser_id_expression): Likewise.
	(cp_parser_unqualified_id): Likewise.  Also for local "id".
	(cp_parser_postfix_expression): Likewise, also for local
	"postfix_expression".  Generate suitable locations for
	C++-style casts, "_Cilk_spawn" constructs.  Convert local
	"initializer" to cp_expr and use it to preserve the location of
	compound literals.  Capture the location of the closing
	parenthesis of a call site via
	cp_parser_parenthesized_expression_list, and use it to build
	a source range for a call.  Use cp_expr in ternary expression.
	(cp_parser_postfix_dot_deref_expression): Convert param from tree to
	cp_expr.  Generate and set a location.
	(cp_parser_parenthesized_expression_list): Add "close_paren_loc"
	out-param, and write back to it.
	(cp_parser_unary_expression): Convert return type from tree to
	cp_expr.  Also for locals "cast_expression" and "expression".
	Generate and use suitable locations for addresses of
	labels and for cast expressions.  Call cp_expr::set_location where
	necessary.  Preserve the locations of negated numeric literals.
	(cp_parser_new_expression): Generate meaningful locations/ranges.
	(cp_parser_cast_expression): Convert return type from tree to
	cp_expr; also for local "expr".  Use the paren location to generate a
	meaningful range for the expression.
	(cp_parser_binary_expression): Convert return type from tree to
	cp_expr; also for local "rhs".  Generate a meaningful location
	for the expression, and use it.  Replace call to
	protected_set_expr_location by converting a build2 to a build2_loc
	and using the location in the call to build_x_binary_op, adding a
	cp_expr::set_location to the latter case.
	(cp_parser_question_colon_clause): Convert param from tree to
	cp_expr; also for local "assignment_expr".  Set the spelling range
	of the expression.
	(cp_parser_assignment_expression): Likewise for return type and
	locals "expr" and "rhs".  Build a meaningful spelling range for
	the expression.  Remove saving of input_location in favor of a
	call to cp_expr::set_location.
	(cp_parser_expression): Convert return type and locals
	"expression" and "assignment_expression" to cp_expr.  Build a
	meaningful spelling range for assignment expressions.
	(cp_parser_constant_expression): Likewise for return type and
	local "expression".
	(cp_parser_builtin_offsetof): Convert return type and local "expr"
	to cp_expr.  Generate suitable locations.
	(cp_parser_lambda_expression): Convert return return type to
	cp_expr.
	(cp_parser_operator_function_id): Likewise.
	(cp_parser_operator): Likewise.  Generate a meaningful range,
	using cp_expr's ctor to return it.
	(cp_parser_initializer_clause): Likewise for local "initializer".
	(cp_parser_braced_list): Likewise for return type.  Generate
	suitable locations.
	(cp_parser_lookup_name): Likewise for return type.  Use cp_expr's
	ctor to preserve the location_t of the name.
	(cp_parser_simple_cast_expression): Likewise for return type.
	(cp_parser_functional_cast): Convert return type and local "cast"
	to cp_expr.  Generate suitable locations.
	(cp_parser_objc_expression): Convert return type to cp_expr.k  Generate
	(cp_parser_objc_message_expression): Generate suitable locations.
	(cp_parser_objc_encode_expression): Convert return type to
	cp_expr.  Generate suitable locations.
	(cp_parser_objc_protocol_expression): Generate suitable locations.
	(cp_parser_objc_selector_expression): Generate suitable locations.
	(cp_parser_transaction_expression): Issue the tm-not-enabled error
	at the location of the __transaction_foo token, rather than at
	input_location.
	* semantics.c (finish_parenthesized_expr): Convert return type and
	param to cp_expr.  Preserve location.
	(perform_koenig_lookup): Likewise for return type
	and param.
	(finish_increment_expr): Likewise.  Generate suitable locations.
	(finish_unary_op_expr): Likewise for return type and local "result".
	Generate suitable locations.
	(finish_id_expression): Convert return type to cp_expr and use
	cp_expr ctor to preserve location information.
	* typeck.c (build_class_member_access_expr): Convert param to
	cp_expr.
	(finish_class_member_access_expr): Likewise.
	(cp_build_binary_op): Convert a build2 to a build2_loc.
	(build_x_unary_op): Convert param from tree to cp_expr.
	(build_nop): Preserve the location of EXPR.
	(build_c_cast): Provide an overloaded variant that takes a cp_expr
	and returns a cp_expr.
	(build_x_modify_expr): Convert return type from tree to cp_expr.

gcc/testsuite/ChangeLog:
	* g++.dg/plugin/diagnostic-test-expressions-1.C: New file, adapted
	from gcc.dg/plugin/diagnostic-test-expressions-1.c.
	* g++.dg/plugin/plugin.exp (plugin_test_list): Add the above.
	* obj-c++.dg/plugin/diagnostic-test-expressions-1.mm: New file,
	based on objc.dg/plugin/diagnostic-test-expressions-1.m.
	* obj-c++.dg/plugin/plugin.exp: New file, based on
	objc.dg/plugin/plugin.exp.
---
 gcc/convert.c                                      |   9 +-
 gcc/cp/cp-tree.h                                   |  82 ++-
 gcc/cp/name-lookup.c                               |   6 +-
 gcc/cp/name-lookup.h                               |   2 +-
 gcc/cp/parser.c                                    | 569 +++++++++++----
 gcc/cp/semantics.c                                 |  53 +-
 gcc/cp/typeck.c                                    |  42 +-
 .../g++.dg/plugin/diagnostic-test-expressions-1.C  | 775 +++++++++++++++++++++
 gcc/testsuite/g++.dg/plugin/plugin.exp             |   5 +-
 .../plugin/diagnostic-test-expressions-1.mm        |  94 +++
 gcc/testsuite/obj-c++.dg/plugin/plugin.exp         |  90 +++
 gcc/tree.c                                         |  25 +-
 gcc/tree.h                                         |  17 +-
 13 files changed, 1570 insertions(+), 199 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
 create mode 100644 gcc/testsuite/obj-c++.dg/plugin/diagnostic-test-expressions-1.mm
 create mode 100644 gcc/testsuite/obj-c++.dg/plugin/plugin.exp

diff --git a/gcc/convert.c b/gcc/convert.c
index e27a6fe..8fb8624 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -362,10 +362,11 @@ convert_to_real_1 (tree type, tree expr, bool fold_p)
     case REAL_TYPE:
       /* Ignore the conversion if we don't need to store intermediate
 	 results and neither type is a decimal float.  */
-      return build1 ((flag_float_store
-		     || DECIMAL_FLOAT_TYPE_P (type)
-		     || DECIMAL_FLOAT_TYPE_P (itype))
-		     ? CONVERT_EXPR : NOP_EXPR, type, expr);
+      return build1_loc (loc,
+			 (flag_float_store
+			  || DECIMAL_FLOAT_TYPE_P (type)
+			  || DECIMAL_FLOAT_TYPE_P (itype))
+			 ? CONVERT_EXPR : NOP_EXPR, type, expr);
 
     case INTEGER_TYPE:
     case ENUMERAL_TYPE:
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 38ae70f..6ddab8a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -40,6 +40,68 @@ c-common.h, not after.
 #include "c-family/c-common.h"
 #include "diagnostic.h"
 
+/* A tree node, together with a location, so that we can track locations
+   (and ranges) during parsing.
+
+   The location is redundant for node kinds that have locations,
+   but not all node kinds do (e.g. constants, and references to
+   params, locals, etc), so we stash a copy here.  */
+
+class cp_expr
+{
+public:
+  cp_expr () :
+    m_value (NULL), m_loc (UNKNOWN_LOCATION) {}
+
+  cp_expr (tree value) :
+    m_value (value), m_loc (EXPR_LOCATION (m_value)) {}
+
+  cp_expr (tree value, location_t loc):
+    m_value (value), m_loc (loc) {}
+
+  cp_expr (const cp_expr &other) :
+    m_value (other.m_value), m_loc (other.m_loc) {}
+
+  /* Implicit conversions to tree.  */
+  operator tree () const { return m_value; }
+  tree & operator* () { return m_value; }
+  tree & operator-> () { return m_value; }
+
+  tree get_value () const { return m_value; }
+  location_t get_location () const { return m_loc; }
+  location_t get_start () const
+  {
+    source_range src_range = get_range_from_loc (line_table, m_loc);
+    return src_range.m_start;
+  }
+  location_t get_finish () const
+  {
+    source_range src_range = get_range_from_loc (line_table, m_loc);
+    return src_range.m_finish;
+  }
+
+  void set_location (location_t loc)
+  {
+    protected_set_expr_location (m_value, loc);
+    m_loc = loc;
+  }
+
+  void set_range (location_t start, location_t finish)
+  {
+    set_location (make_location (m_loc, start, finish));
+  }
+
+ private:
+  tree m_value;
+  location_t m_loc;
+};
+
+inline bool
+operator == (const cp_expr &lhs, tree rhs)
+{
+  return lhs.get_value () == rhs;
+}
+
 #include "name-lookup.h"
 
 /* Usage of TREE_LANG_FLAG_?:
@@ -6291,7 +6353,7 @@ extern tree finish_asm_stmt			(int, tree, tree, tree, tree,
 						 tree);
 extern tree finish_label_stmt			(tree);
 extern void finish_label_decl			(tree);
-extern tree finish_parenthesized_expr		(tree);
+extern cp_expr finish_parenthesized_expr	(cp_expr);
 extern tree force_paren_expr			(tree);
 extern tree finish_non_static_data_member       (tree, tree, tree);
 extern tree begin_stmt_expr			(void);
@@ -6299,15 +6361,15 @@ extern tree finish_stmt_expr_expr		(tree, tree);
 extern tree finish_stmt_expr			(tree, bool);
 extern tree stmt_expr_value_expr		(tree);
 bool empty_expr_stmt_p				(tree);
-extern tree perform_koenig_lookup		(tree, vec<tree, va_gc> *,
+extern cp_expr perform_koenig_lookup		(cp_expr, vec<tree, va_gc> *,
 						 tsubst_flags_t);
 extern tree finish_call_expr			(tree, vec<tree, va_gc> **, bool,
 						 bool, tsubst_flags_t);
 extern tree finish_template_variable		(tree, tsubst_flags_t = tf_warning_or_error);
-extern tree finish_increment_expr		(tree, enum tree_code);
+extern cp_expr finish_increment_expr		(cp_expr, enum tree_code);
 extern tree finish_this_expr			(void);
 extern tree finish_pseudo_destructor_expr       (tree, tree, tree, location_t);
-extern tree finish_unary_op_expr		(location_t, enum tree_code, tree,
+extern cp_expr finish_unary_op_expr		(location_t, enum tree_code, cp_expr,
 						 tsubst_flags_t);
 extern tree finish_compound_literal		(tree, tree, tsubst_flags_t);
 extern tree finish_fname			(tree);
@@ -6321,7 +6383,7 @@ extern tree finish_base_specifier		(tree, tree, bool);
 extern void finish_member_declaration		(tree);
 extern bool outer_automatic_var_p		(tree);
 extern tree process_outer_var_ref		(tree, tsubst_flags_t);
-extern tree finish_id_expression		(tree, tree, tree,
+extern cp_expr finish_id_expression		(tree, tree, tree,
 						 cp_id_kind *,
 						 bool, bool, bool *,
 						 bool, bool, bool, bool,
@@ -6564,9 +6626,9 @@ extern tree unlowered_expr_type                 (const_tree);
 extern tree decay_conversion			(tree,
                                                  tsubst_flags_t,
                                                  bool = true);
-extern tree build_class_member_access_expr      (tree, tree, tree, bool,
+extern tree build_class_member_access_expr      (cp_expr, tree, tree, bool,
 						 tsubst_flags_t);
-extern tree finish_class_member_access_expr     (tree, tree, bool, 
+extern tree finish_class_member_access_expr     (cp_expr, tree, bool,
 						 tsubst_flags_t);
 extern tree build_x_indirect_ref		(location_t, tree,
 						 ref_operator, tsubst_flags_t);
@@ -6588,7 +6650,7 @@ extern tree build_x_binary_op			(location_t,
 extern tree build_x_array_ref			(location_t, tree, tree,
 						 tsubst_flags_t);
 extern tree build_x_unary_op			(location_t,
-						 enum tree_code, tree,
+						 enum tree_code, cp_expr,
                                                  tsubst_flags_t);
 extern tree cp_build_addr_expr			(tree, tsubst_flags_t);
 extern tree cp_build_unary_op                   (enum tree_code, tree, int, 
@@ -6608,8 +6670,10 @@ extern tree build_static_cast			(tree, tree, tsubst_flags_t);
 extern tree build_reinterpret_cast		(tree, tree, tsubst_flags_t);
 extern tree build_const_cast			(tree, tree, tsubst_flags_t);
 extern tree build_c_cast			(location_t, tree, tree);
+extern cp_expr build_c_cast			(location_t loc, tree type,
+						 cp_expr expr);
 extern tree cp_build_c_cast			(tree, tree, tsubst_flags_t);
-extern tree build_x_modify_expr			(location_t, tree,
+extern cp_expr build_x_modify_expr		(location_t, tree,
 						 enum tree_code, tree,
 						 tsubst_flags_t);
 extern tree cp_build_modify_expr		(tree, enum tree_code, tree,
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index cebe57e..86c07ef 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -5659,7 +5659,7 @@ arg_assoc (struct arg_lookup *k, tree n)
 /* Performs Koenig lookup depending on arguments, where fns
    are the functions found in normal lookup.  */
 
-static tree
+static cp_expr
 lookup_arg_dependent_1 (tree name, tree fns, vec<tree, va_gc> *args)
 {
   struct arg_lookup k;
@@ -5720,10 +5720,10 @@ lookup_arg_dependent_1 (tree name, tree fns, vec<tree, va_gc> *args)
 
 /* Wrapper for lookup_arg_dependent_1.  */
 
-tree
+cp_expr
 lookup_arg_dependent (tree name, tree fns, vec<tree, va_gc> *args)
 {
-  tree ret;
+  cp_expr ret;
   bool subtime;
   subtime = timevar_cond_start (TV_NAME_LOOKUP);
   ret = lookup_arg_dependent_1 (name, fns, args);
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index d430edb..d2453e9 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -347,7 +347,7 @@ extern void do_toplevel_using_decl (tree, tree, tree);
 extern void do_local_using_decl (tree, tree, tree);
 extern tree do_class_using_decl (tree, tree);
 extern void do_using_directive (tree);
-extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
+extern cp_expr lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
 extern bool is_associated_namespace (tree, tree);
 extern void parse_using_directive (tree, tree);
 extern tree innermost_non_namespace_value (tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index b4ecac7..d859a89 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -1793,7 +1793,7 @@ struct cp_parser_expression_stack_entry
 {
   /* Left hand side of the binary operation we are currently
      parsing.  */
-  tree lhs;
+  cp_expr lhs;
   /* Original tree code for left hand side, if it was a binary
      expression itself (used for -Wparentheses).  */
   enum tree_code lhs_type;
@@ -1947,15 +1947,15 @@ static cp_parser *cp_parser_new
 
 /* Lexical conventions [gram.lex]  */
 
-static tree cp_parser_identifier
+static cp_expr cp_parser_identifier
   (cp_parser *);
-static tree cp_parser_string_literal
+static cp_expr cp_parser_string_literal
   (cp_parser *, bool, bool, bool);
-static tree cp_parser_userdef_char_literal
+static cp_expr cp_parser_userdef_char_literal
   (cp_parser *);
 static tree cp_parser_userdef_string_literal
   (tree);
-static tree cp_parser_userdef_numeric_literal
+static cp_expr cp_parser_userdef_numeric_literal
   (cp_parser *);
 
 /* Basic concepts [gram.basic]  */
@@ -1965,11 +1965,11 @@ static bool cp_parser_translation_unit
 
 /* Expressions [gram.expr]  */
 
-static tree cp_parser_primary_expression
+static cp_expr cp_parser_primary_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
-static tree cp_parser_id_expression
+static cp_expr cp_parser_id_expression
   (cp_parser *, bool, bool, bool *, bool, bool);
-static tree cp_parser_unqualified_id
+static cp_expr cp_parser_unqualified_id
   (cp_parser *, bool, bool, bool, bool);
 static tree cp_parser_nested_name_specifier_opt
   (cp_parser *, bool, bool, bool, bool);
@@ -1977,19 +1977,19 @@ static tree cp_parser_nested_name_specifier
   (cp_parser *, bool, bool, bool, bool);
 static tree cp_parser_qualifying_entity
   (cp_parser *, bool, bool, bool, bool, bool);
-static tree cp_parser_postfix_expression
+static cp_expr cp_parser_postfix_expression
   (cp_parser *, bool, bool, bool, bool, cp_id_kind *);
 static tree cp_parser_postfix_open_square_expression
   (cp_parser *, tree, bool, bool);
 static tree cp_parser_postfix_dot_deref_expression
-  (cp_parser *, enum cpp_ttype, tree, bool, cp_id_kind *, location_t);
+  (cp_parser *, enum cpp_ttype, cp_expr, bool, cp_id_kind *, location_t);
 static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
-  (cp_parser *, int, bool, bool, bool *);
+  (cp_parser *, int, bool, bool, bool *, location_t * = NULL);
 /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
 enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
 static void cp_parser_pseudo_destructor_name
   (cp_parser *, tree, tree *, tree *);
-static tree cp_parser_unary_expression
+static cp_expr cp_parser_unary_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
 static enum tree_code cp_parser_unary_operator
   (cp_token *);
@@ -2007,23 +2007,23 @@ static vec<tree, va_gc> *cp_parser_new_initializer
   (cp_parser *);
 static tree cp_parser_delete_expression
   (cp_parser *);
-static tree cp_parser_cast_expression
+static cp_expr cp_parser_cast_expression
   (cp_parser *, bool, bool, bool, cp_id_kind *);
-static tree cp_parser_binary_expression
+static cp_expr cp_parser_binary_expression
   (cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *);
 static tree cp_parser_question_colon_clause
-  (cp_parser *, tree);
-static tree cp_parser_assignment_expression
+  (cp_parser *, cp_expr);
+static cp_expr cp_parser_assignment_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
 static enum tree_code cp_parser_assignment_operator_opt
   (cp_parser *);
-static tree cp_parser_expression
+static cp_expr cp_parser_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false);
-static tree cp_parser_constant_expression
+static cp_expr cp_parser_constant_expression
   (cp_parser *, bool = false, bool * = NULL);
-static tree cp_parser_builtin_offsetof
+static cp_expr cp_parser_builtin_offsetof
   (cp_parser *);
-static tree cp_parser_lambda_expression
+static cp_expr cp_parser_lambda_expression
   (cp_parser *);
 static void cp_parser_lambda_introducer
   (cp_parser *, tree);
@@ -2178,9 +2178,9 @@ static void cp_parser_function_body
   (cp_parser *, bool);
 static tree cp_parser_initializer
   (cp_parser *, bool *, bool *);
-static tree cp_parser_initializer_clause
+static cp_expr cp_parser_initializer_clause
   (cp_parser *, bool *);
-static tree cp_parser_braced_list
+static cp_expr cp_parser_braced_list
   (cp_parser*, bool*);
 static vec<constructor_elt, va_gc> *cp_parser_initializer_list
   (cp_parser *, bool *);
@@ -2249,9 +2249,9 @@ static tree cp_parser_mem_initializer_id
 
 /* Overloading [gram.over] */
 
-static tree cp_parser_operator_function_id
+static cp_expr cp_parser_operator_function_id
   (cp_parser *);
-static tree cp_parser_operator
+static cp_expr cp_parser_operator
   (cp_parser *);
 
 /* Templates [gram.temp] */
@@ -2389,7 +2389,7 @@ static tree cp_parser_objc_message_args
   (cp_parser *);
 static tree cp_parser_objc_message_expression
   (cp_parser *);
-static tree cp_parser_objc_encode_expression
+static cp_expr cp_parser_objc_encode_expression
   (cp_parser *);
 static tree cp_parser_objc_defs_expression
   (cp_parser *);
@@ -2397,7 +2397,7 @@ static tree cp_parser_objc_protocol_expression
   (cp_parser *);
 static tree cp_parser_objc_selector_expression
   (cp_parser *);
-static tree cp_parser_objc_expression
+static cp_expr cp_parser_objc_expression
   (cp_parser *);
 static bool cp_parser_objc_selector_p
   (enum cpp_ttype);
@@ -2422,7 +2422,7 @@ static tree cp_parser_objc_struct_declaration
 
 /* Utility Routines */
 
-static tree cp_parser_lookup_name
+static cp_expr cp_parser_lookup_name
   (cp_parser *, tree, enum tag_types, bool, bool, bool, tree *, location_t);
 static tree cp_parser_lookup_name_simple
   (cp_parser *, tree, location_t);
@@ -2432,7 +2432,7 @@ static bool cp_parser_check_declarator_template_parameters
   (cp_parser *, cp_declarator *, location_t);
 static bool cp_parser_check_template_parameters
   (cp_parser *, unsigned, location_t, cp_declarator *);
-static tree cp_parser_simple_cast_expression
+static cp_expr cp_parser_simple_cast_expression
   (cp_parser *);
 static tree cp_parser_global_scope_opt
   (cp_parser *, bool);
@@ -2448,7 +2448,7 @@ static void cp_parser_perform_template_parameter_access_checks
   (vec<deferred_access_check, va_gc> *);
 static tree cp_parser_single_declaration
   (cp_parser *, vec<deferred_access_check, va_gc> *, bool, bool, bool *);
-static tree cp_parser_functional_cast
+static cp_expr cp_parser_functional_cast
   (cp_parser *, tree);
 static tree cp_parser_save_member_function_body
   (cp_parser *, cp_decl_specifier_seq *, cp_declarator *, tree);
@@ -3681,7 +3681,7 @@ cp_parser_pop_lexer (cp_parser *parser)
 /* Parse an identifier.  Returns an IDENTIFIER_NODE representing the
    identifier.  */
 
-static tree
+static cp_expr
 cp_parser_identifier (cp_parser* parser)
 {
   cp_token *token;
@@ -3689,7 +3689,10 @@ cp_parser_identifier (cp_parser* parser)
   /* Look for the identifier.  */
   token = cp_parser_require (parser, CPP_NAME, RT_NAME);
   /* Return the value.  */
-  return token ? token->u.value : error_mark_node;
+  if (token)
+    return cp_expr (token->u.value, token->location);
+  else
+    return error_mark_node;
 }
 
 /* Parse a sequence of adjacent string constants.  Returns a
@@ -3706,7 +3709,7 @@ cp_parser_identifier (cp_parser* parser)
    This code is largely lifted from lex_string() in c-lex.c.
 
    FUTURE: ObjC++ will need to handle @-strings here.  */
-static tree
+static cp_expr
 cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 			  bool lookup_udlit = true)
 {
@@ -3728,6 +3731,8 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
       return error_mark_node;
     }
 
+  location_t loc = tok->location;
+
   if (cpp_userdef_string_p (tok->type))
     {
       string_tree = USERDEF_LITERAL_VALUE (tok->u.value);
@@ -3765,11 +3770,13 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
     }
   else
     {
+      location_t last_tok_loc;
       gcc_obstack_init (&str_ob);
       count = 0;
 
       do
 	{
+	  last_tok_loc = tok->location;
 	  cp_lexer_consume_token (parser->lexer);
 	  count++;
 	  str.text = (const unsigned char *)TREE_STRING_POINTER (string_tree);
@@ -3824,6 +3831,11 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
 	}
       while (cp_parser_is_string_literal (tok));
 
+      /* A string literal built by concatenation has its caret=start at
+	 the start of the initial string, and its finish at the finish of
+	 the final string literal.  */
+      loc = make_location (loc, loc, get_finish (last_tok_loc));
+
       strs = (cpp_string *) obstack_finish (&str_ob);
     }
 
@@ -3876,7 +3888,7 @@ cp_parser_string_literal (cp_parser *parser, bool translate, bool wide_ok,
   if (count > 1)
     obstack_free (&str_ob, 0);
 
-  return value;
+  return cp_expr (value, loc);
 }
 
 /* Look up a literal operator with the name and the exact arguments.  */
@@ -3927,7 +3939,7 @@ lookup_literal_operator (tree name, vec<tree, va_gc> *args)
 /* Parse a user-defined char constant.  Returns a call to a user-defined
    literal operator taking the character as an argument.  */
 
-static tree
+static cp_expr
 cp_parser_userdef_char_literal (cp_parser *parser)
 {
   cp_token *token = cp_lexer_consume_token (parser->lexer);
@@ -4019,7 +4031,7 @@ make_string_pack (tree value)
 /* Parse a user-defined numeric constant.  returns a call to a user-defined
    literal operator.  */
 
-static tree
+static cp_expr
 cp_parser_userdef_numeric_literal (cp_parser *parser)
 {
   cp_token *token = cp_lexer_consume_token (parser->lexer);
@@ -4272,12 +4284,13 @@ cp_parser_end_tentative_firewall (cp_parser *parser, cp_token_position start,
 /* Parse a GNU statement-expression, i.e. ({ stmts }), except for the
    enclosing parentheses.  */
 
-static tree
+static cp_expr
 cp_parser_statement_expr (cp_parser *parser)
 {
   cp_token_position start = cp_parser_start_tentative_firewall (parser);
 
   /* Consume the '('.  */
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
   cp_lexer_consume_token (parser->lexer);
   /* Start the statement-expression.  */
   tree expr = begin_stmt_expr ();
@@ -4286,11 +4299,13 @@ cp_parser_statement_expr (cp_parser *parser)
   /* Finish up.  */
   expr = finish_stmt_expr (expr, false);
   /* Consume the ')'.  */
+  location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location;
   if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
     cp_parser_skip_to_end_of_statement (parser);
 
   cp_parser_end_tentative_firewall (parser, start, expr);
-  return expr;
+  location_t combined_loc = make_location (start_loc, start_loc, finish_loc);
+  return cp_expr (expr, combined_loc);
 }
 
 /* Expressions [gram.expr] */
@@ -4420,7 +4435,7 @@ cp_parser_fold_operator (cp_parser *parser)
 
    Note that the '(' and ')' are matched in primary expression. */
 
-static tree
+static cp_expr
 cp_parser_fold_expression (cp_parser *parser, tree expr1)
 {
   cp_id_kind pidk;
@@ -4540,7 +4555,7 @@ cp_parser_fold_expression (cp_parser *parser, tree expr1)
    Returns a representation of the expression.  Upon return, *IDK
    indicates what kind of id-expression (if any) was present.  */
 
-static tree
+static cp_expr
 cp_parser_primary_expression (cp_parser *parser,
 			      bool address_p,
 			      bool cast_p,
@@ -4625,7 +4640,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	  if (!cast_p)
 	    cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
 	}
-      return token->u.value;
+      return cp_expr (token->u.value, token->location);
 
     case CPP_CHAR_USERDEF:
     case CPP_CHAR16_USERDEF:
@@ -4683,9 +4698,11 @@ cp_parser_primary_expression (cp_parser *parser,
 	}
       /* Otherwise it's a normal parenthesized expression.  */
       {
-	tree expr;
+	cp_expr expr;
 	bool saved_greater_than_is_operator_p;
 
+	location_t open_paren_loc = token->location;
+
 	/* Consume the `('.  */
 	cp_lexer_consume_token (parser->lexer);
 	/* Within a parenthesized expression, a `>' token is always
@@ -4730,7 +4747,11 @@ cp_parser_primary_expression (cp_parser *parser,
 	   template-parameter-list now.  */
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
+
 	/* Consume the `)'.  */
+	token = cp_lexer_peek_token (parser->lexer);
+	location_t close_paren_loc = token->location;
+	expr.set_range (open_paren_loc, close_paren_loc);
 	if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)
 	    && !cp_parser_uncommitted_to_tentative_parse_p (parser))
 	  cp_parser_skip_to_end_of_statement (parser);
@@ -4750,7 +4771,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	      return msg;
 	    /* ... else, fall though to see if it's a lambda.  */
 	  }
-	tree lam = cp_parser_lambda_expression (parser);
+	cp_expr lam = cp_parser_lambda_expression (parser);
 	/* Don't warn about a failed tentative parse.  */
 	if (cp_parser_error_occurred (parser))
 	  return error_mark_node;
@@ -4771,20 +4792,20 @@ cp_parser_primary_expression (cp_parser *parser,
 	  /* These two are the boolean literals.  */
 	case RID_TRUE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_true_node;
+	  return cp_expr (boolean_true_node, token->location);
 	case RID_FALSE:
 	  cp_lexer_consume_token (parser->lexer);
-	  return boolean_false_node;
+	  return cp_expr (boolean_false_node, token->location);
 
 	  /* The `__null' literal.  */
 	case RID_NULL:
 	  cp_lexer_consume_token (parser->lexer);
-	  return null_node;
+	  return cp_expr (null_node, token->location);
 
 	  /* The `nullptr' literal.  */
 	case RID_NULLPTR:
 	  cp_lexer_consume_token (parser->lexer);
-	  return nullptr_node;
+	  return cp_expr (nullptr_node, token->location);
 
 	  /* Recognize the `this' keyword.  */
 	case RID_THIS:
@@ -4798,7 +4819,7 @@ cp_parser_primary_expression (cp_parser *parser,
 	  /* Pointers cannot appear in constant-expressions.  */
 	  if (cp_parser_non_integral_constant_expression (parser, NIC_THIS))
 	    return error_mark_node;
-	  return finish_this_expr ();
+	  return cp_expr (finish_this_expr (), token->location);
 
 	  /* The `operator' keyword can be the beginning of an
 	     id-expression.  */
@@ -4847,7 +4868,8 @@ cp_parser_primary_expression (cp_parser *parser,
 	    tree expression;
 	    tree type;
 	    source_location type_location;
-
+	    location_t start_loc
+	      = cp_lexer_peek_token (parser->lexer)->location;
 	    /* The `__builtin_va_arg' construct is used to handle
 	       `va_arg'.  Consume the `__builtin_va_arg' token.  */
 	    cp_lexer_consume_token (parser->lexer);
@@ -4861,13 +4883,22 @@ cp_parser_primary_expression (cp_parser *parser,
 	    /* Parse the type-id.  */
 	    type = cp_parser_type_id (parser);
 	    /* Look for the closing `)'.  */
+	    location_t finish_loc
+	      = cp_lexer_peek_token (parser->lexer)->location;
 	    cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
 	    /* Using `va_arg' in a constant-expression is not
 	       allowed.  */
 	    if (cp_parser_non_integral_constant_expression (parser,
 							    NIC_VA_ARG))
 	      return error_mark_node;
-	    return build_x_va_arg (type_location, expression, type);
+	    /* Construct a location of the form:
+		 __builtin_va_arg (v, int)
+		 ~~~~~~~~~~~~~~~~~~~~~^~~~
+	       with the caret at the type, ranging from the start of the
+	       "__builtin_va_arg" token to the close paren.  */
+	    location_t combined_loc
+	      = make_location (type_location, start_loc, finish_loc);
+	    return build_x_va_arg (combined_loc, expression, type);
 	  }
 
 	case RID_OFFSETOF:
@@ -4932,14 +4963,14 @@ cp_parser_primary_expression (cp_parser *parser,
     case CPP_TEMPLATE_ID:
     case CPP_NESTED_NAME_SPECIFIER:
       {
-	tree id_expression;
-	tree decl;
+      id_expression:
+	cp_expr id_expression;
+	cp_expr decl;
 	const char *error_msg;
 	bool template_p;
 	bool done;
 	cp_token *id_expr_token;
 
-      id_expression:
 	/* Parse the id-expression.  */
 	id_expression
 	  = cp_parser_id_expression (parser,
@@ -5004,12 +5035,36 @@ cp_parser_primary_expression (cp_parser *parser,
 		if (component == error_mark_node)
 		  return error_mark_node;
 
-		return objc_build_class_component_ref (id_expression, component);
+		tree result = objc_build_class_component_ref (id_expression,
+							      component);
+		/* Build a location of the form:
+		     expr.component
+		     ~~~~~^~~~~~~~~
+		   with caret at the start of the component name (at
+		   input_location), ranging from the start of the id_expression
+		   to the end of the component name.  */
+		location_t combined_loc
+		  = make_location (input_location, id_expression.get_start (),
+				   get_finish (input_location));
+		protected_set_expr_location (result, combined_loc);
+		return result;
 	      }
 
 	    /* In Objective-C++, an instance variable (ivar) may be preferred
-	       to whatever cp_parser_lookup_name() found.  */
-	    decl = objc_lookup_ivar (decl, id_expression);
+	       to whatever cp_parser_lookup_name() found.
+	       Call objc_lookup_ivar.  To avoid exposing cp_expr to the
+	       rest of c-family, we have to do a little extra work to preserve
+	       any location information in cp_expr "decl".  Given that
+	       objc_lookup_ivar is implemented in "c-family" and "objc", we
+	       have a trip through the pure "tree" type, rather than cp_expr.
+	       Naively copying it back to "decl" would implicitly give the
+	       new cp_expr value an UNKNOWN_LOCATION for nodes that don't
+	       store an EXPR_LOCATION.  Hence we only update "decl" (and
+	       hence its location_t) if we get back a different tree node.  */
+	    tree decl_tree = objc_lookup_ivar (decl.get_value (),
+					       id_expression);
+	    if (decl_tree != decl.get_value ())
+	      decl = cp_expr (decl_tree);
 
 	    /* If name lookup gives us a SCOPE_REF, then the
 	       qualifying scope was dependent.  */
@@ -5050,7 +5105,7 @@ cp_parser_primary_expression (cp_parser *parser,
 		  {
 		    error_at (id_expr_token->location,
 			      "local variable %qD may not appear in this context",
-			      decl);
+			      decl.get_value ());
 		    return error_mark_node;
 		  }
 	      }
@@ -5068,6 +5123,7 @@ cp_parser_primary_expression (cp_parser *parser,
                  id_expr_token->location));
 	if (error_msg)
 	  cp_parser_error (parser, error_msg);
+	decl.set_location (id_expr_token->location);
 	return decl;
       }
 
@@ -5078,7 +5134,7 @@ cp_parser_primary_expression (cp_parser *parser,
     }
 }
 
-static inline tree
+static inline cp_expr
 cp_parser_primary_expression (cp_parser *parser,
 			      bool address_p,
 			      bool cast_p,
@@ -5123,7 +5179,7 @@ cp_parser_primary_expression (cp_parser *parser,
    If DECLARATOR_P is true, the id-expression is appearing as part of
    a declarator, rather than as part of an expression.  */
 
-static tree
+static cp_expr
 cp_parser_id_expression (cp_parser *parser,
 			 bool template_keyword_p,
 			 bool check_dependency_p,
@@ -5258,7 +5314,7 @@ cp_parser_id_expression (cp_parser *parser,
    is true, the unqualified-id is appearing as part of a declarator,
    rather than as part of an expression.  */
 
-static tree
+static cp_expr
 cp_parser_unqualified_id (cp_parser* parser,
 			  bool template_keyword_p,
 			  bool check_dependency_p,
@@ -5521,7 +5577,7 @@ cp_parser_unqualified_id (cp_parser* parser,
     case CPP_KEYWORD:
       if (token->keyword == RID_OPERATOR)
 	{
-	  tree id;
+	  cp_expr id;
 
 	  /* This could be a template-id, so we try that first.  */
 	  cp_parser_parse_tentatively (parser);
@@ -6111,7 +6167,7 @@ cp_parser_compound_literal_p (cp_parser *parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
                               bool member_access_only_p, bool decltype_p,
 			      cp_id_kind * pidk_return)
@@ -6120,13 +6176,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
   location_t loc;
   enum rid keyword;
   cp_id_kind idk = CP_ID_KIND_NONE;
-  tree postfix_expression = NULL_TREE;
+  cp_expr postfix_expression = NULL_TREE;
   bool is_member_access = false;
   int saved_in_statement = -1;
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
   loc = token->location;
+  location_t start_loc = get_range_from_loc (line_table, loc).m_start;
+
   /* Some of the productions are determined by keywords.  */
   keyword = token->keyword;
   switch (keyword)
@@ -6137,7 +6195,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
     case RID_CONSTCAST:
       {
 	tree type;
-	tree expression;
+	cp_expr expression;
 	const char *saved_message;
 	bool saved_in_type_id_in_expr_p;
 
@@ -6170,7 +6228,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	/* And the expression which is being cast.  */
 	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
 	expression = cp_parser_expression (parser, & idk, /*cast_p=*/true);
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	cp_token *close_paren = cp_parser_require (parser, CPP_CLOSE_PAREN,
+						   RT_CLOSE_PAREN);
+	location_t end_loc = close_paren ?
+	  close_paren->location : UNKNOWN_LOCATION;
 
 	parser->greater_than_is_operator_p
 	  = saved_greater_than_is_operator_p;
@@ -6203,6 +6264,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  default:
 	    gcc_unreachable ();
 	  }
+
+	/* Construct a location e.g. :
+	     reinterpret_cast <int *> (expr)
+	     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	   ranging from the start of the "*_cast" token to the final closing
+	   paren, with the caret at the start.  */
+	location_t cp_cast_loc = make_location (start_loc, start_loc, end_loc);
+	postfix_expression.set_location (cp_cast_loc);
       }
       break;
 
@@ -6271,6 +6340,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 
     case RID_CILK_SPAWN:
       {
+	location_t cilk_spawn_loc
+	  = cp_lexer_peek_token (parser->lexer)->location;
 	cp_lexer_consume_token (parser->lexer);
 	token = cp_lexer_peek_token (parser->lexer);
 	if (token->type == CPP_SEMICOLON)
@@ -6312,8 +6383,17 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  }
 	else
 	  {
+	    location_t loc = postfix_expression.get_location ();
 	    postfix_expression = build_cilk_spawn (token->location, 
 						   postfix_expression);
+	    /* Build a location of the form:
+		 _Cilk_spawn expr
+		 ~~~~~~~~~~~~^~~~
+	       with caret at the expr, ranging from the start of the
+	       _Cilk_spawn token to the end of the expression.  */
+	    location_t combined_loc =
+	      make_location (loc, cilk_spawn_loc, get_finish (loc));
+	    postfix_expression.set_location (combined_loc);
 	    if (postfix_expression != error_mark_node) 
 	      SET_EXPR_LOCATION (postfix_expression, input_location);
 	    parser->in_statement = parser->in_statement & ~IN_CILK_SPAWN;
@@ -6380,7 +6460,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	if (cp_parser_allow_gnu_extensions_p (parser)
 	    && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
 	  {
-	    tree initializer = NULL_TREE;
+	    cp_expr initializer = NULL_TREE;
 
 	    cp_parser_parse_tentatively (parser);
 
@@ -6435,6 +6515,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 		postfix_expression
 		  = finish_compound_literal (type, initializer,
 					     tf_warning_or_error);
+		postfix_expression.set_location (initializer.get_location ());
 		break;
 	      }
 	  }
@@ -6482,6 +6563,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 							postfix_expression,
 							false,
 							decltype_p);
+	  postfix_expression.set_range (start_loc,
+					postfix_expression.get_location ());
+
 	  idk = CP_ID_KIND_NONE;
           is_member_access = false;
 	  break;
@@ -6495,6 +6579,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    bool saved_non_integral_constant_expression_p = false;
 	    tsubst_flags_t complain = complain_flags (decltype_p);
 	    vec<tree, va_gc> *args;
+	    location_t close_paren_loc;
 
             is_member_access = false;
 
@@ -6513,7 +6598,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    args = (cp_parser_parenthesized_expression_list
 		    (parser, non_attr,
 		     /*cast_p=*/false, /*allow_expansion_p=*/true,
-		     /*non_constant_p=*/NULL));
+		     /*non_constant_p=*/NULL,
+		     /*close_paren_loc=*/&close_paren_loc));
 	    if (is_builtin_constant_p)
 	      {
 		parser->integral_constant_expression_p
@@ -6655,7 +6741,10 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 				    koenig_p,
 				    complain);
 
-	    protected_set_expr_location (postfix_expression, token->location);
+	    location_t combined_loc = make_location (token->location,
+						     start_loc,
+						     close_paren_loc);
+	    postfix_expression.set_location (combined_loc);
 
 	    /* The POSTFIX_EXPRESSION is certainly no longer an id.  */
 	    idk = CP_ID_KIND_NONE;
@@ -6716,7 +6805,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  if (pidk_return != NULL)
 	    * pidk_return = idk;
           if (member_access_only_p)
-            return is_member_access? postfix_expression : error_mark_node;
+            return is_member_access
+              ? postfix_expression
+              : cp_expr (error_mark_node);
           else
             return postfix_expression;
 	}
@@ -6916,7 +7007,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 static tree
 cp_parser_postfix_dot_deref_expression (cp_parser *parser,
 					enum cpp_ttype token_type,
-					tree postfix_expression,
+					cp_expr postfix_expression,
 					bool for_offsetof, cp_id_kind *idk,
 					location_t location)
 {
@@ -6924,6 +7015,7 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
   bool dependent_p;
   bool pseudo_destructor_p;
   tree scope = NULL_TREE;
+  location_t start_loc = postfix_expression.get_start ();
 
   /* If this is a `->' operator, dereference the pointer.  */
   if (token_type == CPP_DEREF)
@@ -6953,7 +7045,7 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
       if (scope == unknown_type_node)
 	{
 	  error_at (location, "%qE does not have class type",
-		    postfix_expression);
+		    postfix_expression.get_value ());
 	  scope = NULL_TREE;
 	}
       /* Unlike the object expression in other contexts, *this is not
@@ -7070,6 +7162,16 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
 	    = finish_class_member_access_expr (postfix_expression, name,
 					       template_p, 
 					       tf_warning_or_error);
+	  /* Build a location e.g.:
+	       ptr->access_expr
+	       ~~~^~~~~~~~~~~~~
+	     where the caret is at the deref token, ranging from
+	     the start of postfix_expression to the end of the access expr.  */
+	  location_t end_loc
+	    = get_finish (cp_lexer_previous_token (parser->lexer)->location);
+	  location_t combined_loc
+	    = make_location (input_location, start_loc, end_loc);
+	  protected_set_expr_location (postfix_expression, combined_loc);
 	}
     }
 
@@ -7118,7 +7220,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 int is_attribute_list,
 					 bool cast_p,
                                          bool allow_expansion_p,
-					 bool *non_constant_p)
+					 bool *non_constant_p,
+					 location_t *close_paren_loc)
 {
   vec<tree, va_gc> *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
@@ -7222,6 +7325,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 	cp_lexer_consume_token (parser->lexer);
       }
 
+  if (close_paren_loc)
+    *close_paren_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
     {
       int ending;
@@ -7391,7 +7497,7 @@ cp_parser_pseudo_destructor_name (cp_parser* parser,
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 			    bool address_p, bool cast_p, bool decltype_p)
 {
@@ -7589,14 +7695,22 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	{
 	  tree identifier;
 	  tree expression;
-	  location_t loc = token->location;
+	  location_t start_loc = token->location;
 
 	  /* Consume the '&&' token.  */
 	  cp_lexer_consume_token (parser->lexer);
 	  /* Look for the identifier.  */
+	  location_t finish_loc
+	    = get_finish (cp_lexer_peek_token (parser->lexer)->location);
 	  identifier = cp_parser_identifier (parser);
+	  /* Construct a location of the form:
+	       &&label
+	       ^~~~~~~
+	     with caret==start at the "&&", finish at the end of the label.  */
+	  location_t combined_loc
+	    = make_location (start_loc, start_loc, finish_loc);
 	  /* Create an expression representing the address.  */
-	  expression = finish_label_address_expr (identifier, loc);
+	  expression = finish_label_address_expr (identifier, combined_loc);
 	  if (cp_parser_non_integral_constant_expression (parser,
 							  NIC_ADDR_LABEL))
 	    expression = error_mark_node;
@@ -7605,8 +7719,8 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
     }
   if (unary_operator != ERROR_MARK)
     {
-      tree cast_expression;
-      tree expression = error_mark_node;
+      cp_expr cast_expression;
+      cp_expr expression = error_mark_node;
       non_integral_constant non_constant_p = NIC_NONE;
       location_t loc = token->location;
       tsubst_flags_t complain = complain_flags (decltype_p);
@@ -7622,6 +7736,14 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 				     /*cast_p=*/false,
 				     /*decltype*/false,
 				     pidk);
+
+      /* Make a location:
+	    OP_TOKEN  CAST_EXPRESSION
+	    ^~~~~~~~~~~~~~~~~~~~~~~~~
+	 with start==caret at the operator token, and
+	 extending to the end of the cast_expression.  */
+      loc = make_location (loc, loc, cast_expression.get_finish ());
+
       /* Now, build an appropriate representation.  */
       switch (unary_operator)
 	{
@@ -7630,6 +7752,9 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	  expression = build_x_indirect_ref (loc, cast_expression,
 					     RO_UNARY_STAR,
                                              complain);
+          /* TODO: build_x_indirect_ref does not always honor the
+             location, so ensure it is set.  */
+          expression.set_location (loc);
 	  break;
 
 	case ADDR_EXPR:
@@ -7639,6 +7764,9 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	  expression = build_x_unary_op (loc, unary_operator,
 					 cast_expression,
                                          complain);
+          /* TODO: build_x_unary_op does not always honor the location,
+             so ensure it is set.  */
+          expression.set_location (loc);
 	  break;
 
 	case PREINCREMENT_EXPR:
@@ -7659,7 +7787,7 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 					 cast_expression);
 	      if (CONSTANT_CLASS_P (folded) && !TREE_OVERFLOW (folded))
 		{
-		  expression = folded;
+		  expression = cp_expr (folded, loc);
 		  break;
 		}
 	    }
@@ -7737,6 +7865,8 @@ cp_parser_new_expression (cp_parser* parser)
   tree nelts = NULL_TREE;
   tree ret;
 
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   /* Look for the optional `::' operator.  */
   global_scope_p
     = (cp_parser_global_scope_opt (parser,
@@ -7821,9 +7951,19 @@ cp_parser_new_expression (cp_parser* parser)
     }
   else
     {
+      /* Construct a location e.g.:
+           ptr = new int[100]
+                 ^~~~~~~~~~~~
+         with caret == start at the start of the "new" token, and the end
+         at the end of the final token we consumed.  */
+      cp_token *end_tok = cp_lexer_previous_token (parser->lexer);
+      location_t end_loc = get_finish (end_tok->location);
+      location_t combined_loc = make_location (start_loc, start_loc, end_loc);
+
       /* Create a representation of the new-expression.  */
       ret = build_new (&placement, type, nelts, &initializer, global_scope_p,
 		       tf_warning_or_error);
+      protected_set_expr_location (ret, combined_loc);
     }
 
   if (placement != NULL)
@@ -8203,7 +8343,7 @@ cp_parser_tokens_start_cast_expression (cp_parser *parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 			   bool decltype_p, cp_id_kind * pidk)
 {
@@ -8211,7 +8351,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
     {
       tree type = NULL_TREE;
-      tree expr = NULL_TREE;
+      cp_expr expr (NULL_TREE);
       int cast_expression = 0;
       const char *saved_message;
 
@@ -8224,7 +8364,9 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
       parser->type_definition_forbidden_message
 	= G_("types may not be defined in casts");
       /* Consume the `('.  */
-      cp_lexer_consume_token (parser->lexer);
+      cp_token *open_paren = cp_lexer_consume_token (parser->lexer);
+      location_t open_paren_loc = open_paren->location;
+
       /* A very tricky bit is that `(struct S) { 3 }' is a
 	 compound-literal (which we permit in C++ as an extension).
 	 But, that construct is not a cast-expression -- it is a
@@ -8326,7 +8468,15 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 		return error_mark_node;
 
 	      /* Perform the cast.  */
-	      expr = build_c_cast (input_location, type, expr);
+	      /* Make a location:
+		   (TYPE) EXPR
+		   ^~~~~~~~~~~
+		 with start==caret at the open paren, extending to the
+		 end of "expr".  */
+	      location_t cast_loc = make_location (open_paren_loc,
+						   open_paren_loc,
+						   expr.get_finish ());
+	      expr = build_c_cast (cast_loc, type, expr);
 	      return expr;
 	    }
 	}
@@ -8419,7 +8569,7 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
  ? PREC_NOT_OPERATOR					     \
  : binops_by_token[token->type].prec)
 
-static tree
+static cp_expr
 cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 			     bool no_toplevel_fold_p,
 			     bool decltype_p,
@@ -8429,7 +8579,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
   cp_parser_expression_stack stack;
   cp_parser_expression_stack_entry *sp = &stack[0];
   cp_parser_expression_stack_entry current;
-  tree rhs;
+  cp_expr rhs;
   cp_token *token;
   enum tree_code rhs_type;
   enum cp_parser_prec new_prec, lookahead_prec;
@@ -8569,6 +8719,11 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 				      maybe_constant_value (rhs));
 
       overload = NULL;
+
+      location_t combined_loc = make_location (current.loc,
+					       current.lhs.get_start (),
+					       rhs.get_finish ());
+
       /* ??? Currently we pass lhs_type == ERROR_MARK and rhs_type ==
 	 ERROR_MARK for everything that is not a binary expression.
 	 This makes warn_about_parentheses miss some warnings that
@@ -8579,18 +8734,22 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
       if (no_toplevel_fold_p
 	  && lookahead_prec <= current.prec
 	  && sp == stack)
-	current.lhs = build2 (current.tree_type,
-			      TREE_CODE_CLASS (current.tree_type)
-			      == tcc_comparison
-			      ? boolean_type_node : TREE_TYPE (current.lhs),
-			      current.lhs, rhs);
+	current.lhs = build2_loc (combined_loc,
+				  current.tree_type,
+				  TREE_CODE_CLASS (current.tree_type)
+				  == tcc_comparison
+				  ? boolean_type_node : TREE_TYPE (current.lhs),
+				  current.lhs, rhs);
       else
-	current.lhs = build_x_binary_op (current.loc, current.tree_type,
-					 current.lhs, current.lhs_type,
-					 rhs, rhs_type, &overload,
-					 complain_flags (decltype_p));
+        {
+          current.lhs = build_x_binary_op (combined_loc, current.tree_type,
+                                           current.lhs, current.lhs_type,
+                                           rhs, rhs_type, &overload,
+                                           complain_flags (decltype_p));
+          /* TODO: build_x_binary_op doesn't always honor the location.  */
+          current.lhs.set_location (combined_loc);
+        }
       current.lhs_type = current.tree_type;
-      protected_set_expr_location (current.lhs, current.loc);
 
       /* If the binary operator required the use of an overloaded operator,
 	 then this expression cannot be an integral constant-expression.
@@ -8607,7 +8766,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
   return current.lhs;
 }
 
-static tree
+static cp_expr
 cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 			     bool no_toplevel_fold_p,
 			     enum cp_parser_prec prec,
@@ -8631,10 +8790,10 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
      ? : assignment-expression */
 
 static tree
-cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
+cp_parser_question_colon_clause (cp_parser* parser, cp_expr logical_or_expr)
 {
   tree expr, folded_logical_or_expr = cp_fully_fold (logical_or_expr);
-  tree assignment_expr;
+  cp_expr assignment_expr;
   struct cp_token *token;
   location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
@@ -8673,6 +8832,15 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
   c_inhibit_evaluation_warnings -=
     folded_logical_or_expr == truthvalue_true_node;
 
+  /* Make a location:
+       LOGICAL_OR_EXPR ? EXPR : ASSIGNMENT_EXPR
+       ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
+     with the caret at the "?", ranging from the start of
+     the logical_or_expr to the end of the assignment_expr.  */
+  loc = make_location (loc,
+		       logical_or_expr.get_start (),
+		       assignment_expr.get_finish ());
+
   /* Build the conditional-expression.  */
   return build_x_conditional_expr (loc, logical_or_expr,
 				   expr,
@@ -8692,11 +8860,11 @@ cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
 
    Returns a representation for the expression.  */
 
-static tree
+static cp_expr
 cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 				 bool cast_p, bool decltype_p)
 {
-  tree expr;
+  cp_expr expr;
 
   /* If the next token is the `throw' keyword, then we're looking at
      a throw-expression.  */
@@ -8725,10 +8893,10 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 	  if (assignment_operator != ERROR_MARK)
 	    {
 	      bool non_constant_p;
-	      location_t saved_input_location;
 
 	      /* Parse the right-hand side of the assignment.  */
-	      tree rhs = cp_parser_initializer_clause (parser, &non_constant_p);
+	      cp_expr rhs = cp_parser_initializer_clause (parser,
+							  &non_constant_p);
 
 	      if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
 		maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
@@ -8739,14 +8907,22 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 							      NIC_ASSIGNMENT))
 		return error_mark_node;
 	      /* Build the assignment expression.  Its default
-		 location is the location of the '=' token.  */
-	      saved_input_location = input_location;
-	      input_location = loc;
+		 location:
+		   LHS = RHS
+		   ~~~~^~~~~
+		 is the location of the '=' token as the
+		 caret, ranging from the start of the lhs to the
+		 end of the rhs.  */
+	      loc = make_location (loc,
+				   expr.get_start (),
+				   rhs.get_finish ());
 	      expr = build_x_modify_expr (loc, expr,
 					  assignment_operator,
 					  rhs,
 					  complain_flags (decltype_p));
-	      input_location = saved_input_location;
+              /* TODO: build_x_modify_expr doesn't honor the location,
+                 so we must set it here.  */
+              expr.set_location (loc);
 	    }
 	}
     }
@@ -8855,16 +9031,16 @@ cp_parser_assignment_operator_opt (cp_parser* parser)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
 		      bool cast_p, bool decltype_p)
 {
-  tree expression = NULL_TREE;
+  cp_expr expression = NULL_TREE;
   location_t loc = UNKNOWN_LOCATION;
 
   while (true)
     {
-      tree assignment_expression;
+      cp_expr assignment_expression;
 
       /* Parse the next assignment-expression.  */
       assignment_expression
@@ -8886,9 +9062,17 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
       if (!expression)
 	expression = assignment_expression;
       else
-	expression = build_x_compound_expr (loc, expression,
-					    assignment_expression,
-					    complain_flags (decltype_p));
+	{
+	  /* Create a location with caret at the comma, ranging
+	     from the start of the LHS to the end of the RHS.  */
+	  loc = make_location (loc,
+			       expression.get_start (),
+			       assignment_expression.get_finish ());
+	  expression = build_x_compound_expr (loc, expression,
+					      assignment_expression,
+					      complain_flags (decltype_p));
+	  expression.set_location (loc);
+	}
       /* If the next token is not a comma, or we're in a fold-expression, then
 	 we are done with the expression.  */
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)
@@ -8915,7 +9099,7 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk,
   constant, *NON_CONSTANT_P is set to TRUE.  If ALLOW_NON_CONSTANT_P
   is false, NON_CONSTANT_P should be NULL.  */
 
-static tree
+static cp_expr
 cp_parser_constant_expression (cp_parser* parser,
 			       bool allow_non_constant_p,
 			       bool *non_constant_p)
@@ -8923,7 +9107,7 @@ cp_parser_constant_expression (cp_parser* parser,
   bool saved_integral_constant_expression_p;
   bool saved_allow_non_integral_constant_expression_p;
   bool saved_non_integral_constant_expression_p;
-  tree expression;
+  cp_expr expression;
 
   /* It might seem that we could simply parse the
      conditional-expression, and then check to see if it were
@@ -8996,13 +9180,15 @@ cp_parser_constant_expression (cp_parser* parser,
      | offsetof-member-designator "[" expression "]"
      | offsetof-member-designator "->" id-expression  */
 
-static tree
+static cp_expr
 cp_parser_builtin_offsetof (cp_parser *parser)
 {
   int save_ice_p, save_non_ice_p;
-  tree type, expr;
+  tree type;
+  cp_expr expr;
   cp_id_kind dummy;
   cp_token *token;
+  location_t finish_loc;
 
   /* We're about to accept non-integral-constant things, but will
      definitely yield an integral constant expression.  Save and
@@ -9010,6 +9196,8 @@ cp_parser_builtin_offsetof (cp_parser *parser)
   save_ice_p = parser->integral_constant_expression_p;
   save_non_ice_p = parser->non_integral_constant_expression_p;
 
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
   /* Consume the "__builtin_offsetof" token.  */
   cp_lexer_consume_token (parser->lexer);
   /* Consume the opening `('.  */
@@ -9055,6 +9243,7 @@ cp_parser_builtin_offsetof (cp_parser *parser)
 
 	case CPP_CLOSE_PAREN:
 	  /* Consume the ")" token.  */
+	  finish_loc = cp_lexer_peek_token (parser->lexer)->location;
 	  cp_lexer_consume_token (parser->lexer);
 	  goto success;
 
@@ -9069,7 +9258,15 @@ cp_parser_builtin_offsetof (cp_parser *parser)
     }
 
  success:
-  expr = finish_offsetof (expr, loc);
+  /* Make a location of the form:
+       __builtin_offsetof (struct s, f)
+       ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
+     with caret at the type-id, ranging from the start of the
+     "_builtin_offsetof" token to the close paren.  */
+  loc = make_location (loc, start_loc, finish_loc);
+  /* The result will be an INTEGER_CST, so we need to explicitly
+     preserve the location.  */
+  expr = cp_expr (finish_offsetof (expr, loc), loc);
 
  failure:
   parser->integral_constant_expression_p = save_ice_p;
@@ -9293,7 +9490,7 @@ finish_lambda_scope (void)
 
    Returns a representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_lambda_expression (cp_parser* parser)
 {
   tree lambda_expr = build_lambda_expr ();
@@ -13290,7 +13487,7 @@ cp_parser_mem_initializer_id (cp_parser* parser)
    Returns an IDENTIFIER_NODE for the operator which is a
    human-readable spelling of the identifier, e.g., `operator +'.  */
 
-static tree
+static cp_expr
 cp_parser_operator_function_id (cp_parser* parser)
 {
   /* Look for the `operator' keyword.  */
@@ -13330,7 +13527,7 @@ cp_literal_operator_id (const char* name)
    Returns an IDENTIFIER_NODE for the operator which is a
    human-readable spelling of the identifier, e.g., `operator +'.  */
 
-static tree
+static cp_expr
 cp_parser_operator (cp_parser* parser)
 {
   tree id = NULL_TREE;
@@ -13339,6 +13536,9 @@ cp_parser_operator (cp_parser* parser)
 
   /* Peek at the next token.  */
   token = cp_lexer_peek_token (parser->lexer);
+
+  location_t start_loc = token->location;
+
   /* Figure out which operator we have.  */
   switch (token->type)
     {
@@ -13355,7 +13555,7 @@ cp_parser_operator (cp_parser* parser)
 	  break;
 
 	/* Consume the `new' or `delete' token.  */
-	cp_lexer_consume_token (parser->lexer);
+	location_t end_loc = cp_lexer_consume_token (parser->lexer)->location;
 
 	/* Peek at the next token.  */
 	token = cp_lexer_peek_token (parser->lexer);
@@ -13366,7 +13566,8 @@ cp_parser_operator (cp_parser* parser)
 	    /* Consume the `[' token.  */
 	    cp_lexer_consume_token (parser->lexer);
 	    /* Look for the `]' token.  */
-	    cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
+	    end_loc = cp_parser_require (parser, CPP_CLOSE_SQUARE,
+                                         RT_CLOSE_SQUARE)->location;
 	    id = ansi_opname (op == NEW_EXPR
 			      ? VEC_NEW_EXPR : VEC_DELETE_EXPR);
 	  }
@@ -13374,7 +13575,9 @@ cp_parser_operator (cp_parser* parser)
 	else
 	  id = ansi_opname (op);
 
-	return id;
+	location_t loc = make_location (start_loc, start_loc, end_loc);
+
+	return cp_expr (id, loc);
       }
 
     case CPP_PLUS:
@@ -13620,7 +13823,7 @@ cp_parser_operator (cp_parser* parser)
       id = error_mark_node;
     }
 
-  return id;
+  return cp_expr (id, start_loc);
 }
 
 /* Parse a template-declaration.
@@ -20316,10 +20519,10 @@ cp_parser_initializer (cp_parser* parser, bool* is_direct_init,
 
    Otherwise, calls cp_parser_braced_list.  */
 
-static tree
+static cp_expr
 cp_parser_initializer_clause (cp_parser* parser, bool* non_constant_p)
 {
-  tree initializer;
+  cp_expr initializer;
 
   /* Assume the expression is constant.  */
   *non_constant_p = false;
@@ -20352,10 +20555,11 @@ cp_parser_initializer_clause (cp_parser* parser, bool* non_constant_p)
    trailing `,' was provided.  NON_CONSTANT_P is as for
    cp_parser_initializer.  */     
 
-static tree
+static cp_expr
 cp_parser_braced_list (cp_parser* parser, bool* non_constant_p)
 {
   tree initializer;
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
 
   /* Consume the `{' token.  */
   cp_lexer_consume_token (parser->lexer);
@@ -20374,9 +20578,18 @@ cp_parser_braced_list (cp_parser* parser, bool* non_constant_p)
   else
     *non_constant_p = false;
   /* Now, there should be a trailing `}'.  */
+  location_t finish_loc = cp_lexer_peek_token (parser->lexer)->location;
   cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
   TREE_TYPE (initializer) = init_list_type_node;
-  return initializer;
+
+  cp_expr result (initializer);
+  /* Build a location of the form:
+       { ... }
+       ^~~~~~~
+     with caret==start at the open brace, finish at the close brace.  */
+  location_t combined_loc = make_location (start_loc, start_loc, finish_loc);
+  result.set_location (combined_loc);
+  return result;
 }
 
 /* Consume tokens up to, and including, the next non-nested closing `]'.
@@ -24077,7 +24290,7 @@ cp_parser_nested_requirement (cp_parser *parser)
    TREE_LIST of candidates if name-lookup results in an ambiguity, and
    NULL_TREE otherwise.  */
 
-static tree
+static cp_expr
 cp_parser_lookup_name (cp_parser *parser, tree name,
 		       enum tag_types tag_type,
 		       bool is_template,
@@ -24319,7 +24532,7 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
 
   maybe_record_typedef_use (decl);
 
-  return decl;
+  return cp_expr (decl, name_location);
 }
 
 /* Like cp_parser_lookup_name, but for use in the typical case where
@@ -25323,7 +25536,7 @@ cp_parser_single_declaration (cp_parser* parser,
 
 /* Parse a cast-expression that is not the operand of a unary "&".  */
 
-static tree
+static cp_expr
 cp_parser_simple_cast_expression (cp_parser *parser)
 {
   return cp_parser_cast_expression (parser, /*address_p=*/false,
@@ -25333,14 +25546,16 @@ cp_parser_simple_cast_expression (cp_parser *parser)
 /* Parse a functional cast to TYPE.  Returns an expression
    representing the cast.  */
 
-static tree
+static cp_expr
 cp_parser_functional_cast (cp_parser* parser, tree type)
 {
   vec<tree, va_gc> *vec;
   tree expression_list;
-  tree cast;
+  cp_expr cast;
   bool nonconst_p;
 
+  location_t start_loc = input_location;
+
   if (!type)
     type = error_mark_node;
 
@@ -25352,9 +25567,21 @@ cp_parser_functional_cast (cp_parser* parser, tree type)
       CONSTRUCTOR_IS_DIRECT_INIT (expression_list) = 1;
       if (TREE_CODE (type) == TYPE_DECL)
 	type = TREE_TYPE (type);
-      return finish_compound_literal (type, expression_list,
+
+      cast = finish_compound_literal (type, expression_list,
 				      tf_warning_or_error);
-    }
+      /* Create a location of the form:
+	    type_name{i, f}
+	    ^~~~~~~~~~~~~~~
+	 with caret == start at the start of the type name,
+	 finishing at the closing brace.  */
+      location_t finish_loc
+	= get_finish (cp_lexer_previous_token (parser->lexer)->location);
+      location_t combined_loc = make_location (start_loc, start_loc,
+					       finish_loc);
+      cast.set_location (combined_loc);
+      return cast;
+   }
 
 
   vec = cp_parser_parenthesized_expression_list (parser, non_attr,
@@ -25380,6 +25607,16 @@ cp_parser_functional_cast (cp_parser* parser, tree type)
       && cp_parser_non_integral_constant_expression (parser,
 						     NIC_CONSTRUCTOR))
     return error_mark_node;
+
+  /* Create a location of the form:
+       float(i)
+       ^~~~~~~~
+     with caret == start at the start of the type name,
+     finishing at the closing paren.  */
+  location_t finish_loc
+    = get_finish (cp_lexer_previous_token (parser->lexer)->location);
+  location_t combined_loc = make_location (start_loc, start_loc, finish_loc);
+  cast.set_location (combined_loc);
   return cast;
 }
 
@@ -27133,7 +27370,7 @@ cp_parser_allow_gnu_extensions_p (cp_parser* parser)
 
   Returns a tree representation of the expression.  */
 
-static tree
+static cp_expr
 cp_parser_objc_expression (cp_parser* parser)
 {
   /* Try to figure out what kind of declaration is present.  */
@@ -27185,12 +27422,23 @@ cp_parser_objc_message_expression (cp_parser* parser)
 {
   tree receiver, messageargs;
 
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
   cp_lexer_consume_token (parser->lexer);  /* Eat '['.  */
   receiver = cp_parser_objc_message_receiver (parser);
   messageargs = cp_parser_objc_message_args (parser);
+  location_t end_loc = cp_lexer_peek_token (parser->lexer)->location;
   cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
 
-  return objc_build_message_expr (receiver, messageargs);
+  tree result = objc_build_message_expr (receiver, messageargs);
+
+  /* Construct a location e.g.
+       [self func1:5]
+       ^~~~~~~~~~~~~~
+     ranging from the '[' to the ']', with the caret at the start.  */
+  location_t combined_loc = make_location (start_loc, start_loc, end_loc);
+  protected_set_expr_location (result, combined_loc);
+
+  return result;
 }
 
 /* Parse an objc-message-receiver.
@@ -27307,11 +27555,12 @@ cp_parser_objc_message_args (cp_parser* parser)
 
    Returns an encoded representation of the type argument.  */
 
-static tree
+static cp_expr
 cp_parser_objc_encode_expression (cp_parser* parser)
 {
   tree type;
   cp_token *token;
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@encode'.  */
   cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
@@ -27339,7 +27588,16 @@ cp_parser_objc_encode_expression (cp_parser* parser)
       return value;
     }
 
-  return objc_build_encode_expr (type);
+
+  /* Build a location of the form:
+       @encode(int)
+       ^~~~~~~~~~~~
+     with caret==start at the @ token, finishing at the close paren.  */
+  location_t combined_loc
+    = make_location (start_loc, start_loc,
+                     cp_lexer_previous_token (parser->lexer)->location);
+
+  return cp_expr (objc_build_encode_expr (type), combined_loc);
 }
 
 /* Parse an Objective-C @defs expression.  */
@@ -27368,13 +27626,23 @@ static tree
 cp_parser_objc_protocol_expression (cp_parser* parser)
 {
   tree proto;
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@protocol'.  */
   cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
   proto = cp_parser_identifier (parser);
   cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
 
-  return objc_build_protocol_expr (proto);
+  /* Build a location of the form:
+       @protocol(prot)
+       ^~~~~~~~~~~~~~~
+     with caret==start at the @ token, finishing at the close paren.  */
+  location_t combined_loc
+    = make_location (start_loc, start_loc,
+                     cp_lexer_previous_token (parser->lexer)->location);
+  tree result = objc_build_protocol_expr (proto);
+  protected_set_expr_location (result, combined_loc);
+  return result;
 }
 
 /* Parse an Objective-C selector expression.
@@ -27450,7 +27718,18 @@ cp_parser_objc_selector_expression (cp_parser* parser)
  finish_selector:
   cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
 
-  return objc_build_selector_expr (loc, sel_seq);
+
+  /* Build a location of the form:
+       @selector(func)
+       ^~~~~~~~~~~~~~~
+     with caret==start at the @ token, finishing at the close paren.  */
+  location_t combined_loc
+    = make_location (loc, loc,
+                     cp_lexer_previous_token (parser->lexer)->location);
+  tree result = objc_build_selector_expr (combined_loc, sel_seq);
+  /* TODO: objc_build_selector_expr doesn't always honor the location.  */
+  protected_set_expr_location (result, combined_loc);
+  return result;
 }
 
 /* Parse a list of identifiers.
@@ -36286,16 +36565,18 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
   cp_token *token;
   tree expr, noex;
   bool noex_expr;
+  location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
   gcc_assert (keyword == RID_TRANSACTION_ATOMIC
       || keyword == RID_TRANSACTION_RELAXED);
 
   if (!flag_tm)
-    error (keyword == RID_TRANSACTION_RELAXED
-	   ? G_("%<__transaction_relaxed%> without transactional memory "
-		"support enabled")
-	   : G_("%<__transaction_atomic%> without transactional memory "
-		"support enabled"));
+    error_at (loc,
+	      keyword == RID_TRANSACTION_RELAXED
+	      ? G_("%<__transaction_relaxed%> without transactional memory "
+		  "support enabled")
+	      : G_("%<__transaction_atomic%> without transactional memory "
+		   "support enabled"));
 
   token = cp_parser_require_keyword (parser, keyword,
       (keyword == RID_TRANSACTION_ATOMIC ? RT_TRANSACTION_ATOMIC
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 3bb6184..82f7d3a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1672,8 +1672,8 @@ force_paren_expr (tree expr)
 
 /* Finish a parenthesized expression EXPR.  */
 
-tree
-finish_parenthesized_expr (tree expr)
+cp_expr
+finish_parenthesized_expr (cp_expr expr)
 {
   if (EXPR_P (expr))
     /* This inhibits warnings in c_common_truthvalue_conversion.  */
@@ -1688,7 +1688,7 @@ finish_parenthesized_expr (tree expr)
   if (TREE_CODE (expr) == STRING_CST)
     PAREN_STRING_LITERAL_P (expr) = 1;
 
-  expr = force_paren_expr (expr);
+  expr = cp_expr (force_paren_expr (expr), expr.get_location ());
 
   return expr;
 }
@@ -2164,8 +2164,8 @@ empty_expr_stmt_p (tree expr_stmt)
    the function (or functions) to call; ARGS are the arguments to the
    call.  Returns the functions to be considered by overload resolution.  */
 
-tree
-perform_koenig_lookup (tree fn, vec<tree, va_gc> *args,
+cp_expr
+perform_koenig_lookup (cp_expr fn, vec<tree, va_gc> *args,
 		       tsubst_flags_t complain)
 {
   tree identifier = NULL_TREE;
@@ -2460,10 +2460,23 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
    is indicated by CODE, which should be POSTINCREMENT_EXPR or
    POSTDECREMENT_EXPR.)  */
 
-tree
-finish_increment_expr (tree expr, enum tree_code code)
-{
-  return build_x_unary_op (input_location, code, expr, tf_warning_or_error);
+cp_expr
+finish_increment_expr (cp_expr expr, enum tree_code code)
+{
+  /* input_location holds the location of the trailing operator token.
+     Build a location of the form:
+       expr++
+       ~~~~^~
+     with the caret at the operator token, ranging from the start
+     of EXPR to the end of the operator token.  */
+  location_t combined_loc = make_location (input_location,
+					   expr.get_start (),
+					   get_finish (input_location));
+  cp_expr result = build_x_unary_op (combined_loc, code, expr,
+				     tf_warning_or_error);
+  /* TODO: build_x_unary_op doesn't honor the location, so set it here.  */
+  result.set_location (combined_loc);
+  return result;
 }
 
 /* Finish a use of `this'.  Returns an expression for `this'.  */
@@ -2557,11 +2570,21 @@ finish_pseudo_destructor_expr (tree object, tree scope, tree destructor,
 
 /* Finish an expression of the form CODE EXPR.  */
 
-tree
-finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
+cp_expr
+finish_unary_op_expr (location_t op_loc, enum tree_code code, cp_expr expr,
 		      tsubst_flags_t complain)
 {
-  tree result = build_x_unary_op (loc, code, expr, complain);
+  /* Build a location of the form:
+       ++expr
+       ^~~~~~
+     with the caret at the operator token, ranging from the start
+     of the operator token to the end of EXPR.  */
+  location_t combined_loc = make_location (op_loc,
+					   op_loc, expr.get_finish ());
+  cp_expr result = build_x_unary_op (combined_loc, code, expr, complain);
+  /* TODO: build_x_unary_op doesn't always honor the location.  */
+  result.set_location (combined_loc);
+
   tree result_ovl, expr_ovl;
 
   if (!(complain & tf_warning))
@@ -2581,7 +2604,7 @@ finish_unary_op_expr (location_t loc, enum tree_code code, tree expr,
     result_ovl = cp_fully_fold (result_ovl);
 
   if (CONSTANT_CLASS_P (result_ovl) && TREE_OVERFLOW_P (result_ovl))
-    overflow_warning (input_location, result_ovl);
+    overflow_warning (combined_loc, result_ovl);
 
   return result;
 }
@@ -3324,7 +3347,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
    the use of "this" explicit.
 
    Upon return, *IDK will be filled in appropriately.  */
-tree
+cp_expr
 finish_id_expression (tree id_expression,
 		      tree decl,
 		      tree scope,
@@ -3669,7 +3692,7 @@ finish_id_expression (tree id_expression,
 	}
     }
 
-  return decl;
+  return cp_expr (decl, location);
 }
 
 /* Implement the __typeof keyword: Return the type of EXPR, suitable for
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 1d2943f..0a1e446 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2261,7 +2261,7 @@ lookup_anon_field (tree t, tree type)
    functions indicated by MEMBER.  */
 
 tree
-build_class_member_access_expr (tree object, tree member,
+build_class_member_access_expr (cp_expr object, tree member,
 				tree access_path, bool preserve_reference,
 				tsubst_flags_t complain)
 {
@@ -2291,10 +2291,10 @@ build_class_member_access_expr (tree object, tree member,
 	      && CLASS_TYPE_P (TREE_TYPE (object_type)))
 	    error ("request for member %qD in %qE, which is of pointer "
 		   "type %qT (maybe you meant to use %<->%> ?)",
-		   member, object, object_type);
+		   member, object.get_value (), object_type);
 	  else
 	    error ("request for member %qD in %qE, which is of non-class "
-		   "type %qT", member, object, object_type);
+		   "type %qT", member, object.get_value (), object_type);
 	}
       return error_mark_node;
     }
@@ -2624,7 +2624,7 @@ check_template_keyword (tree decl)
    be a template via the use of the "A::template B" syntax.  */
 
 tree
-finish_class_member_access_expr (tree object, tree name, bool template_p,
+finish_class_member_access_expr (cp_expr object, tree name, bool template_p,
 				 tsubst_flags_t complain)
 {
   tree expr;
@@ -2658,7 +2658,7 @@ finish_class_member_access_expr (tree object, tree name, bool template_p,
 	      && TYPE_P (TREE_OPERAND (name, 0))
 	      && dependent_type_p (TREE_OPERAND (name, 0))))
 	return build_min_nt_loc (UNKNOWN_LOCATION, COMPONENT_REF,
-				 object, name, NULL_TREE);
+				 object.get_value (), name, NULL_TREE);
       object = build_non_dependent_expr (object);
     }
   else if (c_dialect_objc ()
@@ -2681,10 +2681,10 @@ finish_class_member_access_expr (tree object, tree name, bool template_p,
 	      && CLASS_TYPE_P (TREE_TYPE (object_type)))
 	    error ("request for member %qD in %qE, which is of pointer "
 		   "type %qT (maybe you meant to use %<->%> ?)",
-		   name, object, object_type);
+		   name, object.get_value (), object_type);
 	  else
 	    error ("request for member %qD in %qE, which is of non-class "
-		   "type %qT", name, object, object_type);
+		   "type %qT", name, object.get_value (), object_type);
 	}
       return error_mark_node;
     }
@@ -5111,7 +5111,7 @@ cp_build_binary_op (location_t location,
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
-  result = build2 (resultcode, build_type, op0, op1);
+  result = build2_loc (location, resultcode, build_type, op0, op1);
   if (final_type != 0)
     result = cp_convert (final_type, result, complain);
 
@@ -5269,7 +5269,7 @@ pointer_diff (tree op0, tree op1, tree ptrtype, tsubst_flags_t complain)
    and XARG is the operand.  */
 
 tree
-build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
+build_x_unary_op (location_t loc, enum tree_code code, cp_expr xarg,
 		  tsubst_flags_t complain)
 {
   tree orig_expr = xarg;
@@ -5279,7 +5279,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
   if (processing_template_decl)
     {
       if (type_dependent_expression_p (xarg))
-	return build_min_nt_loc (loc, code, xarg, NULL_TREE);
+	return build_min_nt_loc (loc, code, xarg.get_value (), NULL_TREE);
 
       xarg = build_non_dependent_expr (xarg);
     }
@@ -5314,7 +5314,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 		error (DECL_CONSTRUCTOR_P (fn)
 		       ? G_("taking address of constructor %qE")
 		       : G_("taking address of destructor %qE"),
-		       xarg);
+		       xarg.get_value ());
 	      return error_mark_node;
 	    }
 	}
@@ -5330,7 +5330,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 	      if (complain & tf_error)
 		{
 		  error ("invalid use of %qE to form a "
-			 "pointer-to-member-function", xarg);
+			 "pointer-to-member-function", xarg.get_value ());
 		  if (TREE_CODE (xarg) != OFFSET_REF)
 		    inform (input_location, "  a qualified-id is required");
 		}
@@ -5341,7 +5341,7 @@ build_x_unary_op (location_t loc, enum tree_code code, tree xarg,
 	      if (complain & tf_error)
 		error ("parentheses around %qE cannot be used to form a"
 		       " pointer-to-member-function",
-		       xarg);
+		       xarg.get_value ());
 	      else
 		return error_mark_node;
 	      PTRMEM_OK_P (xarg) = 1;
@@ -5438,7 +5438,7 @@ build_nop (tree type, tree expr)
 {
   if (type == error_mark_node || error_operand_p (expr))
     return expr;
-  return build1 (NOP_EXPR, type, expr);
+  return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr);
 }
 
 /* Take the address of ARG, whatever that means under C++ semantics.
@@ -7270,6 +7270,18 @@ build_c_cast (location_t /*loc*/, tree type, tree expr)
   return cp_build_c_cast (type, expr, tf_warning_or_error);
 }
 
+/* Like the "build_c_cast" used for c-common, but using cp_expr to
+   preserve location information even for tree nodes that don't
+   support it.  */
+
+cp_expr
+build_c_cast (location_t loc, tree type, cp_expr expr)
+{
+  cp_expr result = cp_build_c_cast (type, expr, tf_warning_or_error);
+  result.set_location (loc);
+  return result;
+}
+
 /* Build an expression representing an explicit C-style cast to type
    TYPE of expression EXPR.  */
 
@@ -7775,7 +7787,7 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
   return result;
 }
 
-tree
+cp_expr
 build_x_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 		     tree rhs, tsubst_flags_t complain)
 {
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
new file mode 100644
index 0000000..826e835
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
@@ -0,0 +1,775 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret -fpermissive" } */
+
+/* This is a collection of unittests to verify that we're correctly
+   capturing the source code ranges of various kinds of expression.
+
+   It uses the various "diagnostic_test_*_expression_range_plugin"
+   plugins which handles "__emit_expression_range" by generating a warning
+   at the given source range of the input argument.  Each of the
+   different plugins do this at a different phase of the internal
+   representation (tree, gimple, etc), so we can verify that the
+   source code range information is valid at each phase.
+
+   We want to accept an expression of any type.  We use variadic arguments.
+   For compatibility with the C tests we have a dummy argument, since
+   C requires at least one argument before the ellipsis.  */
+
+extern void __emit_expression_range (int dummy, ...);
+
+int global;
+
+void test_parentheses (int a, int b)
+{
+  __emit_expression_range (0, (a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) );
+                               ~~~^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, (a + b) * (a - b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a + b) * (a - b) );
+                               ~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, !(a && b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !(a && b) );
+                               ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Postfix expressions.  ************************************************/
+
+void test_array_reference (int *arr)
+{
+  __emit_expression_range (0, arr[100] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, arr[100] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+}
+
+int test_function_call (int p, int q, int r)
+{
+  __emit_expression_range (0, test_function_call (p, q, r) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, test_function_call (p, q, r) );
+                               ~~~~~~~~~~~~~~~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+  return 0;
+}
+
+struct test_struct
+{
+  int field;
+};
+
+int test_structure_references (struct test_struct *ptr)
+{
+  struct test_struct local;
+  local.field = 42;
+
+  __emit_expression_range (0, local.field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, local.field );
+                               ~~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, ptr->field ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ptr->field );
+                               ~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+int test_postfix_incdec (int i)
+{
+  __emit_expression_range (0, i++ ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i++ );
+                               ~^~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, i-- ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i-- );
+                               ~^~
+   { dg-end-multiline-output "" } */
+}
+
+/* Unary operators.  ****************************************************/
+
+int test_prefix_incdec (int i)
+{
+  __emit_expression_range (0, ++i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ++i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, --i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, --i );
+                               ^~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_address_operator (void)
+{
+  __emit_expression_range (0, &global ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &global );
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_indirection (int *ptr)
+{
+  __emit_expression_range (0, *ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *ptr );
+                               ^~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_unary_minus (int i)
+{
+  __emit_expression_range (0, -i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, -i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_ones_complement (int i)
+{
+  __emit_expression_range (0, ~i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ~i );
+                               ^~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_negation (int flag)
+{
+  __emit_expression_range (0, !flag ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, !flag );
+                               ^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Casts.  ****************************************************/
+
+void test_cast (void *ptr)
+{
+  __emit_expression_range (0, (int *)ptr ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (int *)ptr );
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, *(int *)0xdeadbeef ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, *(int *)0xdeadbeef );
+                               ^~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+/* Binary operators.  *******************************************/
+
+void test_multiplicative_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs * rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs * rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs / rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs / rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs % rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs % rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_additive_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs + rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs + rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs - rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs - rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_shift_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs << rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs << rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >> rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >> rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_relational_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs < rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs < rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs > rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs > rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs <= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs <= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs >= rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs >= rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_equality_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs == rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs == rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs != rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs != rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_bitwise_binary_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs & rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs & rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs ^ rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs ^ rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs | rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs | rhs );
+                               ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_logical_operators (int lhs, int rhs)
+{
+  __emit_expression_range (0, lhs && rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs && rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, lhs || rhs ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, lhs || rhs );
+                               ~~~~^~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Conditional operator.  *******************************************/
+
+void test_conditional_operators (int flag, int on_true, int on_false)
+{
+  __emit_expression_range (0, flag ? on_true : on_false ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, flag ? on_true : on_false );
+                               ~~~~~^~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Assignment expressions.  *******************************************/
+
+void test_assignment_expressions (int dest, int other)
+{
+  __emit_expression_range (0, dest = other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest = other );
+                               ~~~~~^~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest *= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest *= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest /= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest /= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest %= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest %= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest += other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest += other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest -= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest -= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest <<= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest <<= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest >>= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest >>= other );
+                               ~~~~~^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest &= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest &= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest ^= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest ^= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, dest |= other ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dest |= other );
+                               ~~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Comma operator.  *******************************************/
+
+void test_comma_operator (int a, int b)
+{
+  __emit_expression_range (0, (a++, a + b) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (a++, a + b) );
+                               ~~~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Literals.  **************************************************/
+
+/* We can't test the ranges of literals directly, since the underlying
+   tree nodes don't retain a location.  However, we can test that they
+   have ranges during parsing by building compound expressions using
+   them, and verifying the ranges of the compound expressions.  */
+
+void test_string_literals (int i)
+{
+  __emit_expression_range (0, "foo"[i] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, "foo"[i] );
+                               ~~~~~~~^
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, &"foo" "bar" ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &"foo" "bar" );
+                               ^~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_numeric_literals (int i)
+{
+  __emit_expression_range (0, 42 + i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 42 + i );
+                               ~~~^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, i + 42 ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i + 42 );
+                               ~~^~~~
+   { dg-end-multiline-output "" } */
+
+  /* Verify locations of negative literals (via folding of
+     unary negation).  */
+
+  __emit_expression_range (0, -42 + i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, -42 + i );
+                               ~~~~^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, i + -42 ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i + -42 );
+                               ~~^~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, i ? 0 : -1 ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, i ? 0 : -1 );
+                               ~~^~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Braced initializers.  ***************************************/
+
+/* We can't test the ranges of these directly, since the underlying
+   tree nodes don't retain a location.  However, we can test that they
+   have ranges during parsing by building compound expressions using
+   them, and verifying the ranges of the compound expressions.  */
+
+#define vector(elcount, type)  \
+__attribute__((vector_size((elcount)*sizeof(type)))) type
+
+void test_braced_init (void)
+{
+  /* Verify start of range.  */
+  __emit_expression_range (0, (vector(4, float)){2., 2., 2., 2.} + 1); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, (vector(4, float)){2., 2., 2., 2.} + 1);
+                                                 ~~~~~~~~~~~~~~~~~^~~
+   { dg-end-multiline-output "" } */
+
+  /* Verify end of range.  */
+  __emit_expression_range (0, 1 + (vector(4, float)){2., 2., 2., 2.}); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 1 + (vector(4, float)){2., 2., 2., 2.});
+                               ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Statement expressions.  ***************************************/
+
+void test_statement_expression (void)
+{
+  __emit_expression_range (0, ({ static int a; a; }) + 1);  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ({ static int a; a; }) + 1);
+                               ~~~~~~~~~~~~~~~~~~~~~~~^~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, 1 + ({ static int a; a; }) );  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, 1 + ({ static int a; a; }) );
+                               ~~^~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Other expressions.  */
+
+void test_address_of_label (void)
+{
+ label:
+  __emit_expression_range (0, &&label );  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, &&label );
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_transaction_expressions (void)
+{
+  int i;
+  i = __transaction_atomic (42); /* { dg-error "without transactional memory support enabled" } */
+/* { dg-begin-multiline-output "" }
+   i = __transaction_atomic (42);
+       ^~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+  i = __transaction_relaxed (42); /* { dg-error "without transactional memory support enabled" } */
+/* { dg-begin-multiline-output "" }
+   i = __transaction_relaxed (42);
+       ^~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_keywords (int i)
+{
+  __emit_expression_range (0, __FUNCTION__[i] );  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, __FUNCTION__[i] );
+                               ~~~~~~~~~~~~~~^
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, __PRETTY_FUNCTION__[i] );  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, __PRETTY_FUNCTION__[i] );
+                               ~~~~~~~~~~~~~~~~~~~~~^
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, __func__[i] );  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, __func__[i] );
+                               ~~~~~~~~~~^
+   { dg-end-multiline-output "" } */
+}
+
+void test_builtin_va_arg (__builtin_va_list v)
+{
+  __emit_expression_range (0,  __builtin_va_arg (v, int) );  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0,  __builtin_va_arg (v, int) );
+                                ~~~~~~~~~~~~~~~~~~~~~^~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0,  __builtin_va_arg (v, int) + 1 );  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0,  __builtin_va_arg (v, int) + 1 );
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
+   { dg-end-multiline-output "" } */
+}
+
+struct s { int i; float f; };
+
+void test_builtin_offsetof (int i)
+{
+  __emit_expression_range (0,  i + __builtin_offsetof (struct s, f) );  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0,  i + __builtin_offsetof (struct s, f) );
+                                ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0,  __builtin_offsetof (struct s, f) + i );  /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0,  __builtin_offsetof (struct s, f) + i );
+                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
+   { dg-end-multiline-output "" } */
+}
+
+/* Examples of non-trivial expressions.  ****************************/
+
+extern double sqrt (double x);
+
+void test_quadratic (double a, double b, double c)
+{
+  __emit_expression_range (0, b * b - 4 * a * c ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, b * b - 4 * a * c );
+                               ~~~~~~^~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0,
+     (-b + sqrt (b * b - 4 * a * c))
+     / (2 * a)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+      (-b + sqrt (b * b - 4 * a * c))
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      / (2 * a));
+      ^~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+int bar (int);
+void test_combinations (int a)
+{
+  __emit_expression_range (0, bar (a) > a ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, bar (a) > a );
+                               ~~~~~~~~^~~
+   { dg-end-multiline-output "" } */
+}
+
+/* C++-specific expresssions. ****************************************/
+
+void test_cp_literal_keywords (int a, int b)
+{
+  this; /* { dg-error "invalid use of 'this' in non-member function" } */
+/* { dg-begin-multiline-output "" }
+   this;
+   ^~~~
+   { dg-end-multiline-output "" } */
+
+}
+
+class base {
+ public:
+  base ();
+  base (int i);
+  virtual ~base ();
+  int pub ();
+private:
+  int priv ();
+};
+class derived : public base { ~derived (); };
+
+void test_cp_casts (base *ptr)
+{
+  __emit_expression_range (0, dynamic_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, dynamic_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, static_cast <derived *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, static_cast <derived *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, reinterpret_cast <int *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, reinterpret_cast <int *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, const_cast <base *> (ptr)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, const_cast <base *> (ptr));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_functional_casts (int i, float f)
+{
+  __emit_expression_range (0, float(i)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, float(i));
+                               ^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, int(f)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, int(f));
+                               ^~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, s{i, f}); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, s{i, f});
+                               ^~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+template <typename TYPENAME>
+class example_template
+{
+public:
+  example_template (TYPENAME v);
+};
+
+void test_new (void)
+{
+  __emit_expression_range (0, ::new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ::new base);
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base);
+                               ^~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base));
+                               ^~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new base (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new base (42));
+                               ^~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  __emit_expression_range (0, new (base) (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new (base) (42));
+                               ^~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+  /* TODO: placement new.  */
+
+  __emit_expression_range (0, new example_template<int> (42)); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, new example_template<int> (42));
+                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_methods ()
+{
+  __emit_expression_range (0, ((base *)1)->pub () ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, ((base *)1)->pub () );
+                               ~~~~~~~~~~~~~~~~~^~
+   { dg-end-multiline-output "" } */
+
+  ((base *)1)->priv (); // { dg-error " is private " }
+/* { dg-begin-multiline-output "" }
+   ((base *)1)->priv ();
+                      ^
+   { dg-end-multiline-output "" }
+   { dg-begin-multiline-output "" }
+   int priv ();
+       ^~~~
+   { dg-end-multiline-output "" } */
+}
+
+class tests
+{
+public:
+  void test_method_calls ();
+  int some_method () const;
+};
+
+void
+tests::test_method_calls ()
+{
+  __emit_expression_range (0, this->some_method () ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range (0, this->some_method () );
+                               ~~~~~~~~~~~~~~~~~~^~
+   { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
index 3ed1397..3be89a0 100644
--- a/gcc/testsuite/g++.dg/plugin/plugin.exp
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -62,7 +62,10 @@ set plugin_test_list [list \
     { dumb_plugin.c dumb-plugin-test-1.C } \
     { header_plugin.c header-plugin-test.C } \
     { decl_plugin.c decl-plugin-test.C } \
-    { def_plugin.c def-plugin-test.C } ]
+    { def_plugin.c def-plugin-test.C } \
+    { ../../gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c \
+	  diagnostic-test-expressions-1.C } \
+]
 
 foreach plugin_test $plugin_test_list {
     # Replace each source file with its full-path name
diff --git a/gcc/testsuite/obj-c++.dg/plugin/diagnostic-test-expressions-1.mm b/gcc/testsuite/obj-c++.dg/plugin/diagnostic-test-expressions-1.mm
new file mode 100644
index 0000000..609fe3d
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/plugin/diagnostic-test-expressions-1.mm
@@ -0,0 +1,94 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret" } */
+
+/* This file is similar to diagnostic-test-expressions-1.c
+   (see the notes in that file); this file adds test
+   coverage for various Objective C constructs. */
+
+extern void __emit_expression_range (int dummy, ...);
+
+@protocol prot
+@end
+
+@interface tests <prot>
+- (int) func0;
+- (int) func1:(int)i;
++ (int) func2;
+- (void) test_sending_messages;
++ (void) test_class_dot_name;
+- (void) test_at_selector;
+- (void) test_at_protocol;
+- (void) test_at_encode:(int)i;
+@end
+
+@implementation tests
+- (int) func0
+{
+  return 42;
+}
+- (int) func1:(int)i
+{
+  return i * i;
+}
++ (int) func2
+{
+  return 0;
+}
+- (void) test_sending_messages
+{
+  __emit_expression_range ( 0, [self func0] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range ( 0, [self func0] );
+                                ^~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+  __emit_expression_range ( 0, [self func1:5] ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range ( 0, [self func1:5] );
+                                ^~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
++ (void) test_class_dot_name
+{
+  __emit_expression_range ( 0, tests.func2 ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range ( 0, tests.func2 );
+                                ~~~~~~^~~~~
+   { dg-end-multiline-output "" } */
+}
+- (void) test_at_selector
+{
+  __emit_expression_range ( 0, @selector(func0) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range ( 0, @selector(func0) );
+                                ^~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+- (void) test_at_protocol
+{
+  __emit_expression_range ( 0, @protocol(prot) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range ( 0, @protocol(prot) );
+                                ^~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+- (void) test_at_encode:(int)i
+{
+  /* @encode() generates a STRING_CST which doesn't retain a location
+     after parsing, so we need to access it via compound expressions
+     that can't be folded away.  */
+
+  /* Verify start.  */
+  __emit_expression_range ( 0, @encode(int) + i ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range ( 0, @encode(int) + i );
+                                ~~~~~~~~~~~~~^~~
+   { dg-end-multiline-output "" } */
+
+  /* Verify finish.  */
+  __emit_expression_range ( 0, i + @encode(int) ); /* { dg-warning "range" } */
+/* { dg-begin-multiline-output "" }
+   __emit_expression_range ( 0, i + @encode(int) );
+                                ~~^~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+@end
diff --git a/gcc/testsuite/obj-c++.dg/plugin/plugin.exp b/gcc/testsuite/obj-c++.dg/plugin/plugin.exp
new file mode 100644
index 0000000..d0d400b
--- /dev/null
+++ b/gcc/testsuite/obj-c++.dg/plugin/plugin.exp
@@ -0,0 +1,90 @@
+#   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Test the functionality of the GCC plugin support
+
+load_lib target-supports.exp
+load_lib obj-c++-dg.exp
+
+global TESTING_IN_BUILD_TREE
+global ENABLE_PLUGIN
+
+# The plugin testcases currently only work when the build tree is available.
+# Also check whether the host supports plugins.
+if { ![info exists TESTING_IN_BUILD_TREE] || ![info exists ENABLE_PLUGIN] } {
+    return
+}
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+    set DEFAULT_CFLAGS " -ansi -pedantic-errors"
+}
+
+# The procedures in plugin-support.exp need these parameters.
+set default_flags $DEFAULT_CFLAGS
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+# Load support procs.
+load_lib plugin-support.exp
+
+# These tests don't run runtest_file_p consistently if it
+# doesn't return the same values, so disable parallelization
+# of this *.exp file.  The first parallel runtest to reach
+# this will run all the tests serially.
+if ![gcc_parallel_test_run_p plugin] {
+    return
+}
+gcc_parallel_test_enable 0
+
+# Specify the plugin source file and the associated test files in a list.
+# plugin_test_list={ {plugin1 test1 test2 ...} {plugin2 test1 ...} ... }
+set plugin_test_list [list \
+    { ../../gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c \
+	  diagnostic-test-expressions-1.mm } \
+]
+
+foreach plugin_test $plugin_test_list {
+    # Replace each source file with its full-path name
+    for {set i 0} {$i < [llength $plugin_test]} {incr i} {
+	set basename [lindex $plugin_test $i]
+	set plugin_test [lreplace $plugin_test $i $i $srcdir/$subdir/$basename]
+    }
+    set plugin_src [lindex $plugin_test 0]
+    # If we're only testing specific files and this isn't one of them, skip it.
+    if ![runtest_file_p $runtests $plugin_src] then {
+        continue
+    }
+    set plugin_input_tests [lreplace $plugin_test 0 0]
+    plugin-test-execute $plugin_src $plugin_input_tests
+}
+
+# run the plugindir tests
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/plugindir*.mm]] \
+	"" $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
+
+gcc_parallel_test_enable 1
diff --git a/gcc/tree.c b/gcc/tree.c
index bd5cf73..c8b3ab8 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13905,7 +13905,7 @@ nonnull_arg_p (const_tree arg)
 /* Given location LOC, strip away any packed range information
    or ad-hoc information.  */
 
-static location_t
+location_t
 get_pure_location (location_t loc)
 {
   if (IS_ADHOC_LOC (loc))
@@ -13935,20 +13935,20 @@ set_block (location_t loc, tree block)
   return COMBINE_LOCATION_DATA (line_table, pure_loc, src_range, block);
 }
 
-void
+location_t
 set_source_range (tree expr, location_t start, location_t finish)
 {
   source_range src_range;
   src_range.m_start = start;
   src_range.m_finish = finish;
-  set_source_range (expr, src_range);
+  return set_source_range (expr, src_range);
 }
 
-void
+location_t
 set_source_range (tree expr, source_range src_range)
 {
   if (!EXPR_P (expr))
-    return;
+    return UNKNOWN_LOCATION;
 
   location_t pure_loc = get_pure_location (EXPR_LOCATION (expr));
   location_t adhoc = COMBINE_LOCATION_DATA (line_table,
@@ -13956,6 +13956,21 @@ set_source_range (tree expr, source_range src_range)
 					    src_range,
 					    NULL);
   SET_EXPR_LOCATION (expr, adhoc);
+  return adhoc;
+}
+
+location_t
+make_location (location_t caret, location_t start, location_t finish)
+{
+  location_t pure_loc = get_pure_location (caret);
+  source_range src_range;
+  src_range.m_start = start;
+  src_range.m_finish = finish;
+  location_t combined_loc = COMBINE_LOCATION_DATA (line_table,
+						   pure_loc,
+						   src_range,
+						   NULL);
+  return combined_loc;
 }
 
 /* Return the name of combined function FN, for debugging purposes.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index a60e9dd..aef825d 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5369,6 +5369,16 @@ type_with_alias_set_p (const_tree t)
   return false;
 }
 
+extern location_t get_pure_location (location_t loc);
+
+/* Get the endpoint of any range encoded within location LOC.  */
+
+static inline location_t
+get_finish (location_t loc)
+{
+  return get_range_from_loc (line_table, loc).m_finish;
+}
+
 extern location_t set_block (location_t loc, tree block);
 
 extern void gt_ggc_mx (tree &);
@@ -5377,10 +5387,10 @@ extern void gt_pch_nx (tree &, gt_pointer_operator, void *);
 
 extern bool nonnull_arg_p (const_tree);
 
-extern void
+extern location_t
 set_source_range (tree expr, location_t start, location_t finish);
 
-extern void
+extern location_t
 set_source_range (tree expr, source_range src_range);
 
 static inline source_range
@@ -5390,6 +5400,9 @@ get_decl_source_range (tree decl)
   return get_range_from_loc (line_table, loc);
 }
 
+extern location_t
+make_location (location_t caret, location_t start, location_t finish);
+
 /* Return true if it makes sense to promote/demote from_type to to_type. */
 inline bool
 desired_pro_or_demotion_p (const_tree to_type, const_tree from_type)
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 07/10] Fix g++.dg/template/ref3.C
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
                                 ` (2 preceding siblings ...)
  2015-12-03 14:37               ` [PATCH 01/10] C++ FE: expression ranges v4 David Malcolm
@ 2015-12-03 14:37               ` David Malcolm
  2015-12-03 20:38                 ` Jason Merrill
  2015-12-03 14:37               ` [PATCH 02/10] Fix g++.dg/cpp0x/nsdmi-template14.C David Malcolm
                                 ` (5 subsequent siblings)
  9 siblings, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:37 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

Testcase g++.dg/template/ref3.C:

     1	// PR c++/28341
     2
     3	template<const int&> struct A {};
     4
     5	template<typename T> struct B
     6	{
     7	  A<(T)0> b; // { dg-error "constant|not a valid" }
     8	  A<T(0)> a; // { dg-error "constant|not a valid" }
     9	};
    10
    11	B<const int&> b;

The output of this test for both c++11 and c++14 is unaffected
by the patch kit:
 g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
 g++.dg/template/ref3.C:11:15:   required from here
 g++.dg/template/ref3.C:7:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
 g++.dg/template/ref3.C:8:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue

However, the c++98 output is changed:

Status quo for c++98:
g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
g++.dg/template/ref3.C:11:15:   required from here
g++.dg/template/ref3.C:7:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
g++.dg/template/ref3.C:8:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression

(line 7 and 8 are at the closing semicolon for fields b and a)

With the patchkit for c++98:
g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
g++.dg/template/ref3.C:11:15:   required from here
g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression

So the 2nd:
  "error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression"
moves from line 8 to line 7 (and moves them to earlier, having ranges)

What's happening is that cp_parser_enclosed_template_argument_list
builds a CAST_EXPR, the first time from cp_parser_cast_expression,
the second time from cp_parser_functional_cast; these have locations
representing the correct respective caret&ranges, i.e.:

   A<(T)0> b;
     ^~~~

and:

   A<T(0)> a;
     ^~~~

Eventually finish_template_type is called for each, to build a RECORD_TYPE,
and we get a cache hit the 2nd time through here in pt.c:
8281	      hash = spec_hasher::hash (&elt);
8282	      entry = type_specializations->find_with_hash (&elt, hash);
8283
8284	      if (entry)
8285		return entry->spec;

due to:
  template_args_equal (ot=<cast_expr 0x7ffff19bc400>, nt=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/pt.c:7778
which calls:
  cp_tree_equal (t1=<cast_expr 0x7ffff19bc400>, t2=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/tree.c:2833
and returns equality.

Hence we get a single RECORD_TYPE for the type A<(T)(0)>, and hence
when issuing the errors it uses the TREE_VEC for the first one,
using the location of the first line.

I'm not sure what the ideal fix for this is; for now I've worked
around it by updating the dg directives to reflect the new output.

gcc/testsuite/ChangeLog:
	* g++.dg/template/ref3.C: Update locations of dg directives.
---
 gcc/testsuite/g++.dg/template/ref3.C | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/gcc/testsuite/g++.dg/template/ref3.C b/gcc/testsuite/g++.dg/template/ref3.C
index 976c093..6e568c3 100644
--- a/gcc/testsuite/g++.dg/template/ref3.C
+++ b/gcc/testsuite/g++.dg/template/ref3.C
@@ -4,8 +4,10 @@ template<const int&> struct A {};
 
 template<typename T> struct B
 {
-  A<(T)0> b; // { dg-error "constant|not a valid" }
-  A<T(0)> a; // { dg-error "constant|not a valid" }
+  A<(T)0> b; // { dg-error "constant" "" { target c++98_only } }
+  // { dg-error "not a valid" "" { target c++11 } 7 }
+
+  A<T(0)> a; // { dg-error "not a valid" "" { target c++11 } }
 };
 
 B<const int&> b;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 08/10] Fix g++.dg/ubsan/pr63956.C
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
                                 ` (5 preceding siblings ...)
  2015-12-03 14:37               ` [PATCH 03/10] Fix g++.dg/gomp/loop-1.C David Malcolm
@ 2015-12-03 14:37               ` David Malcolm
  2015-12-03 14:37               ` [PATCH 05/10] Fix location of dg-error within g++.dg/template/pr64100.C David Malcolm
                                 ` (2 subsequent siblings)
  9 siblings, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:37 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

With the location patch, various errors in g++.dg/ubsan/pr63956.C
change:

     8  constexpr int
     9  fn1 (int a, int b)
    10  {
    11    if (b != 2)
    12      a <<= b;
    13    return a;
    14  }
    15
    16  constexpr int i1 = fn1 (5, 3);
    17  constexpr int i2 = fn1 (5, -2); // { dg-error "is negative" }

Here's the first error as printed by the status quo:
g++.dg/ubsan/pr63956.C:17:24:   in constexpr expansion of ‘fn1(5, -2)’
g++.dg/ubsan/pr63956.C:17:30: error: right operand of shift expression ‘(5 << -2)’ is negative
 constexpr int i2 = fn1 (5, -2); // { dg-error "is negative" }
                              ^

...and with the location patch:
g++.dg/ubsan/pr63956.C:17:24:   in constexpr expansion of ‘fn1(5, -2)’
g++.dg/ubsan/pr63956.C:12:11: error: right operand of shift expression ‘(5 << -2)’ is negative
     a <<= b;
           ^
I believe this is an improvement: we're now identifying both relevant
places, rather than just one, and clearly highlighting the exact
subexpression of interest.

Hence this patch updates the testcase to reflect the improved
location information.

gcc/testsuite/ChangeLog:
	* g++.dg/ubsan/pr63956.C: Update dg directives to reflect
	improved location information.
---
 gcc/testsuite/g++.dg/ubsan/pr63956.C | 28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)

diff --git a/gcc/testsuite/g++.dg/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C
index 185a719..b265631 100644
--- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
+++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
@@ -10,15 +10,18 @@ fn1 (int a, int b)
 {
   if (b != 2)
     a <<= b;
+    // { dg-error "5 << -2.. is negative" "" { target *-*-* } 12 }
+    // { dg-error "is >= than the precision of the left operand" "" { target *-*-* } 12 }
+    // { dg-error "-2 << 4.. is negative" "" { target *-*-* } 12 }
   return a;
 }
 
 constexpr int i1 = fn1 (5, 3);
-constexpr int i2 = fn1 (5, -2); // { dg-error "is negative" }
-constexpr int i3 = fn1 (5, sizeof (int) * __CHAR_BIT__); // { dg-error "is >= than the precision of the left operand" }
-constexpr int i4 = fn1 (5, 256); // { dg-error "is >= than the precision of the left operand" }
+constexpr int i2 = fn1 (5, -2); // { dg-message "in constexpr expansion" }
+constexpr int i3 = fn1 (5, sizeof (int) * __CHAR_BIT__); // { dg-message "in constexpr expansion" }
+constexpr int i4 = fn1 (5, 256); // { dg-message "in constexpr expansion" }
 constexpr int i5 = fn1 (5, 2);
-constexpr int i6 = fn1 (-2, 4); // { dg-error "is negative" }
+constexpr int i6 = fn1 (-2, 4); // { dg-message "in constexpr expansion" }
 constexpr int i7 = fn1 (0, 2);
 
 SA (i1 == 40);
@@ -30,13 +33,16 @@ fn2 (int a, int b)
 {
   if (b != 2)
     a >>= b;
+    // { dg-error "4 >> -1.. is negative" "" { target *-*-* } 35 }
+    // { dg-error "is >= than the precision of the left operand" "" { target *-*-* } 35 }
+
   return a;
 }
 
 constexpr int j1 = fn2 (4, 1);
-constexpr int j2 = fn2 (4, -1); // { dg-error "is negative" }
-constexpr int j3 = fn2 (10, sizeof (int) * __CHAR_BIT__); // { dg-error "is >= than the precision of the left operand" }
-constexpr int j4 = fn2 (1, 256); // { dg-error "is >= than the precision of the left operand" }
+constexpr int j2 = fn2 (4, -1); // { dg-message "in constexpr expansion" }
+constexpr int j3 = fn2 (10, sizeof (int) * __CHAR_BIT__); // { dg-message "in constexpr expansion" }
+constexpr int j4 = fn2 (1, 256); // { dg-message "in constexpr expansion" }
 constexpr int j5 = fn2 (5, 2);
 constexpr int j6 = fn2 (-2, 4);
 constexpr int j7 = fn2 (0, 4);
@@ -49,12 +55,12 @@ constexpr int
 fn3 (int a, int b)
 {
   if (b != 2)
-    a = a / b;
+    a = a / b; // { dg-error "..7 / 0.. is not a constant expression" }
   return a;
 }
 
 constexpr int k1 = fn3 (8, 4);
-constexpr int k2 = fn3 (7, 0); // { dg-error "is not a constant expression" }
+constexpr int k2 = fn3 (7, 0); // { dg-message "in constexpr expansion" }
 constexpr int k3 = fn3 (INT_MIN, -1); // { dg-error "overflow in constant expression" }
 
 SA (k1 == 2);
@@ -63,12 +69,12 @@ constexpr float
 fn4 (float a, float b)
 {
   if (b != 2.0)
-    a = a / b;
+    a = a / b; // { dg-error "is not a constant expression" }
   return a;
 }
 
 constexpr float l1 = fn4 (5.0, 3.0);
-constexpr float l2 = fn4 (7.0, 0.0); // { dg-error "is not a constant expression" }
+constexpr float l2 = fn4 (7.0, 0.0); // { dg-message "in constexpr expansion" }
 
 constexpr int
 fn5 (const int *a, int b)
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 06/10] Fix g++.dg/template/pseudodtor3.C
  2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
                                 ` (8 preceding siblings ...)
  2015-12-03 14:37               ` [PATCH 09/10] Fix g++.dg/warn/pr35635.C David Malcolm
@ 2015-12-03 14:53               ` David Malcolm
  9 siblings, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-03 14:53 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

gcc/testsuite/ChangeLog:
	* g++.dg/template/pseudodtor3.C: Update column numbers in dg-error
	directives.
---
 gcc/testsuite/g++.dg/template/pseudodtor3.C | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gcc/testsuite/g++.dg/template/pseudodtor3.C b/gcc/testsuite/g++.dg/template/pseudodtor3.C
index 202182f..8700bb9 100644
--- a/gcc/testsuite/g++.dg/template/pseudodtor3.C
+++ b/gcc/testsuite/g++.dg/template/pseudodtor3.C
@@ -11,7 +11,7 @@ struct A
 template <typename T> struct B
 {
   T &foo ();
-  B () { foo.~T (); }	// { dg-error "10:invalid use of member" }
+  B () { foo.~T (); }	// { dg-error "15:invalid use of member" }
 };
 
 B<int> b;
@@ -19,7 +19,7 @@ B<int> b;
 template <typename T, typename S> struct C
 {
   T t;
-  C () { t.~S (); }	// { dg-error "10:is not of type" }
+  C () { t.~S (); }	// { dg-error "13:is not of type" }
 };
 
 C<int, long int> c;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 02/10] Fix g++.dg/cpp0x/nsdmi-template14.C
  2015-12-03 14:37               ` [PATCH 02/10] Fix g++.dg/cpp0x/nsdmi-template14.C David Malcolm
@ 2015-12-03 20:33                 ` Jason Merrill
  2015-12-03 21:43                   ` David Malcolm
  0 siblings, 1 reply; 40+ messages in thread
From: Jason Merrill @ 2015-12-03 20:33 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On 12/03/2015 09:55 AM, David Malcolm wrote:
> This patch adds bulletproofing to detect purged tokens, and avoid using
> them.
>
> Alternatively, is it OK to access purged tokens for this kind of thing?
> If so, would it make more sense to instead leave their locations untouched
> when purging them?

I think cp_lexer_previous_token should skip past purged tokens.

Jason

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 07/10] Fix g++.dg/template/ref3.C
  2015-12-03 14:37               ` [PATCH 07/10] Fix g++.dg/template/ref3.C David Malcolm
@ 2015-12-03 20:38                 ` Jason Merrill
  2015-12-03 22:08                   ` David Malcolm
  0 siblings, 1 reply; 40+ messages in thread
From: Jason Merrill @ 2015-12-03 20:38 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On 12/03/2015 09:55 AM, David Malcolm wrote:
> Testcase g++.dg/template/ref3.C:
>
>       1	// PR c++/28341
>       2
>       3	template<const int&> struct A {};
>       4
>       5	template<typename T> struct B
>       6	{
>       7	  A<(T)0> b; // { dg-error "constant|not a valid" }
>       8	  A<T(0)> a; // { dg-error "constant|not a valid" }
>       9	};
>      10
>      11	B<const int&> b;
>
> The output of this test for both c++11 and c++14 is unaffected
> by the patch kit:
>   g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
>   g++.dg/template/ref3.C:11:15:   required from here
>   g++.dg/template/ref3.C:7:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
>   g++.dg/template/ref3.C:8:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
>
> However, the c++98 output is changed:
>
> Status quo for c++98:
> g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
> g++.dg/template/ref3.C:11:15:   required from here
> g++.dg/template/ref3.C:7:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> g++.dg/template/ref3.C:8:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>
> (line 7 and 8 are at the closing semicolon for fields b and a)
>
> With the patchkit for c++98:
> g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
> g++.dg/template/ref3.C:11:15:   required from here
> g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>
> So the 2nd:
>    "error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression"
> moves from line 8 to line 7 (and moves them to earlier, having ranges)
>
> What's happening is that cp_parser_enclosed_template_argument_list
> builds a CAST_EXPR, the first time from cp_parser_cast_expression,
> the second time from cp_parser_functional_cast; these have locations
> representing the correct respective caret&ranges, i.e.:
>
>     A<(T)0> b;
>       ^~~~
>
> and:
>
>     A<T(0)> a;
>       ^~~~
>
> Eventually finish_template_type is called for each, to build a RECORD_TYPE,
> and we get a cache hit the 2nd time through here in pt.c:
> 8281	      hash = spec_hasher::hash (&elt);
> 8282	      entry = type_specializations->find_with_hash (&elt, hash);
> 8283
> 8284	      if (entry)
> 8285		return entry->spec;
>
> due to:
>    template_args_equal (ot=<cast_expr 0x7ffff19bc400>, nt=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/pt.c:7778
> which calls:
>    cp_tree_equal (t1=<cast_expr 0x7ffff19bc400>, t2=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/tree.c:2833
> and returns equality.
>
> Hence we get a single RECORD_TYPE for the type A<(T)(0)>, and hence
> when issuing the errors it uses the TREE_VEC for the first one,
> using the location of the first line.

Why does the type sharing affect where the parser gives the error?

> I'm not sure what the ideal fix for this is; for now I've worked
> around it by updating the dg directives to reflect the new output.
>
> gcc/testsuite/ChangeLog:
> 	* g++.dg/template/ref3.C: Update locations of dg directives.
> ---
>   gcc/testsuite/g++.dg/template/ref3.C | 6 ++++--
>   1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/gcc/testsuite/g++.dg/template/ref3.C b/gcc/testsuite/g++.dg/template/ref3.C
> index 976c093..6e568c3 100644
> --- a/gcc/testsuite/g++.dg/template/ref3.C
> +++ b/gcc/testsuite/g++.dg/template/ref3.C
> @@ -4,8 +4,10 @@ template<const int&> struct A {};
>
>   template<typename T> struct B
>   {
> -  A<(T)0> b; // { dg-error "constant|not a valid" }
> -  A<T(0)> a; // { dg-error "constant|not a valid" }
> +  A<(T)0> b; // { dg-error "constant" "" { target c++98_only } }
> +  // { dg-error "not a valid" "" { target c++11 } 7 }
> +
> +  A<T(0)> a; // { dg-error "not a valid" "" { target c++11 } }
>   };
>
>   B<const int&> b;
>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 02/10] Fix g++.dg/cpp0x/nsdmi-template14.C
  2015-12-03 20:33                 ` Jason Merrill
@ 2015-12-03 21:43                   ` David Malcolm
  2015-12-03 22:17                     ` Jason Merrill
  0 siblings, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-12-03 21:43 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Thu, 2015-12-03 at 15:33 -0500, Jason Merrill wrote:
> On 12/03/2015 09:55 AM, David Malcolm wrote:
> > This patch adds bulletproofing to detect purged tokens, and avoid using
> > them.
> >
> > Alternatively, is it OK to access purged tokens for this kind of thing?
> > If so, would it make more sense to instead leave their locations untouched
> > when purging them?
> 
> I think cp_lexer_previous_token should skip past purged tokens.

Sorry if this is a silly question, but should I limit the iteration e.g.
by detecting a sentinel value?  e.g.
  parser->lexer->buffer->address () ?

Or is there guaranteed to be an unpurged token somewhere beforehand?

Out of interest, the prior tokens here are:

(gdb) p end_tok[0]
$25 = {type = CPP_GREATER, keyword = RID_MAX, flags = 0 '\000',
pragma_kind = PRAGMA_NONE, implicit_extern_c = 0, 
  error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value
= 0x0, value = <tree 0x0>}}

(gdb) p end_tok[-1]
$26 = {type = CPP_NAME, keyword = RID_MAX, flags = 0 '\000', pragma_kind
= PRAGMA_NONE, implicit_extern_c = 0, 
  error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value
= 0x0, value = <tree 0x0>}}

(gdb) p end_tok[-2]
$27 = {type = CPP_LESS, keyword = RID_MAX, flags = 0 '\000', pragma_kind
= PRAGMA_NONE, implicit_extern_c = 0, 
  error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value
= 0x0, value = <tree 0x0>}}

(gdb) p end_tok[-3]
$28 = {type = 86, keyword = RID_MAX, flags = 1 '\001', pragma_kind =
PRAGMA_NONE, implicit_extern_c = 0, error_reported = 0, 
  purged_p = 0, location = 202016, u = {tree_check_value =
0x7ffff19dfd98, value = <bdver1-direct,bdver1-agu 0x7ffff19dfd98>}}

(gdb) p end_tok[-4]
$29 = {type = CPP_KEYWORD, keyword = RID_NEW, flags = 1 '\001',
pragma_kind = PRAGMA_NONE, implicit_extern_c = 0, 
  error_reported = 0, purged_p = 0, location = 201890, u =
{tree_check_value = 0x7ffff18a8318, 
    value = <identifier_node 0x7ffff18a8318 new>}}

where the previous unpurged token is:

(gdb) p end_tok[-3].purged_p
$31 = 0

(gdb) call inform (end_tok[-3].location, "")
../../src/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C:11:14: note:
   B* p = new B<N>;
              ^

which would give a range of:

   B* p = new B<N>;
          ^~~~~

for the erroneous new expression, rather than:


   B* p = new B<N>;
          ^~~~~~~~

if we used the location of the purged token (the CPP_GREATER).
I prefer the latter, hence my suggestion about not zero-ing out the
locations of tokens when purging them.


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 07/10] Fix g++.dg/template/ref3.C
  2015-12-03 20:38                 ` Jason Merrill
@ 2015-12-03 22:08                   ` David Malcolm
  2015-12-04 16:01                     ` Jason Merrill
  0 siblings, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-12-03 22:08 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Thu, 2015-12-03 at 15:38 -0500, Jason Merrill wrote:
> On 12/03/2015 09:55 AM, David Malcolm wrote:
> > Testcase g++.dg/template/ref3.C:
> >
> >       1	// PR c++/28341
> >       2
> >       3	template<const int&> struct A {};
> >       4
> >       5	template<typename T> struct B
> >       6	{
> >       7	  A<(T)0> b; // { dg-error "constant|not a valid" }
> >       8	  A<T(0)> a; // { dg-error "constant|not a valid" }
> >       9	};
> >      10
> >      11	B<const int&> b;
> >
> > The output of this test for both c++11 and c++14 is unaffected
> > by the patch kit:
> >   g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
> >   g++.dg/template/ref3.C:11:15:   required from here
> >   g++.dg/template/ref3.C:7:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
> >   g++.dg/template/ref3.C:8:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
> >
> > However, the c++98 output is changed:
> >
> > Status quo for c++98:
> > g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
> > g++.dg/template/ref3.C:11:15:   required from here
> > g++.dg/template/ref3.C:7:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> > g++.dg/template/ref3.C:8:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> >
> > (line 7 and 8 are at the closing semicolon for fields b and a)
> >
> > With the patchkit for c++98:
> > g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
> > g++.dg/template/ref3.C:11:15:   required from here
> > g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> > g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> >
> > So the 2nd:
> >    "error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression"
> > moves from line 8 to line 7 (and moves them to earlier, having ranges)
> >
> > What's happening is that cp_parser_enclosed_template_argument_list
> > builds a CAST_EXPR, the first time from cp_parser_cast_expression,
> > the second time from cp_parser_functional_cast; these have locations
> > representing the correct respective caret&ranges, i.e.:
> >
> >     A<(T)0> b;
> >       ^~~~
> >
> > and:
> >
> >     A<T(0)> a;
> >       ^~~~
> >
> > Eventually finish_template_type is called for each, to build a RECORD_TYPE,
> > and we get a cache hit the 2nd time through here in pt.c:
> > 8281	      hash = spec_hasher::hash (&elt);
> > 8282	      entry = type_specializations->find_with_hash (&elt, hash);
> > 8283
> > 8284	      if (entry)
> > 8285		return entry->spec;
> >
> > due to:
> >    template_args_equal (ot=<cast_expr 0x7ffff19bc400>, nt=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/pt.c:7778
> > which calls:
> >    cp_tree_equal (t1=<cast_expr 0x7ffff19bc400>, t2=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/tree.c:2833
> > and returns equality.
> >
> > Hence we get a single RECORD_TYPE for the type A<(T)(0)>, and hence
> > when issuing the errors it uses the TREE_VEC for the first one,
> > using the location of the first line.
> 
> Why does the type sharing affect where the parser gives the error?

I believe what's happening is that the patchkit is setting location_t
values for more expressions than before, including the expression for
the template param.  pt.c:tsubst_expr has this:

  if (EXPR_HAS_LOCATION (t))
    input_location = EXPR_LOCATION (t);

I believe that before (in the status quo), the substituted types didn't
have location_t values, and hence the above conditional didn't fire;
input_location was coming from a *token* where the expansion happened,
hence we got an error message on the relevant line for each expansion.

With the patch, the substituted types have location_t values within
their params, hence the conditional above fires: input_location is
updated to use the EXPR_LOCATION, which comes from that of the param
within the type - but with type-sharing it's using the first place where
the type is created.

Perhaps a better fix is for cp_parser_non_integral_constant_expression
to take a location_t, rather than have it rely on input_location?


> > I'm not sure what the ideal fix for this is; for now I've worked
> > around it by updating the dg directives to reflect the new output.
> >
> > gcc/testsuite/ChangeLog:
> > 	* g++.dg/template/ref3.C: Update locations of dg directives.
> > ---
> >   gcc/testsuite/g++.dg/template/ref3.C | 6 ++++--
> >   1 file changed, 4 insertions(+), 2 deletions(-)
> >
> > diff --git a/gcc/testsuite/g++.dg/template/ref3.C b/gcc/testsuite/g++.dg/template/ref3.C
> > index 976c093..6e568c3 100644
> > --- a/gcc/testsuite/g++.dg/template/ref3.C
> > +++ b/gcc/testsuite/g++.dg/template/ref3.C
> > @@ -4,8 +4,10 @@ template<const int&> struct A {};
> >
> >   template<typename T> struct B
> >   {
> > -  A<(T)0> b; // { dg-error "constant|not a valid" }
> > -  A<T(0)> a; // { dg-error "constant|not a valid" }
> > +  A<(T)0> b; // { dg-error "constant" "" { target c++98_only } }
> > +  // { dg-error "not a valid" "" { target c++11 } 7 }
> > +
> > +  A<T(0)> a; // { dg-error "not a valid" "" { target c++11 } }
> >   };
> >
> >   B<const int&> b;
> >
> 


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 02/10] Fix g++.dg/cpp0x/nsdmi-template14.C
  2015-12-03 21:43                   ` David Malcolm
@ 2015-12-03 22:17                     ` Jason Merrill
  2015-12-04 14:22                       ` [PATCH 02/10 v2] Fix g++.dg/cpp0x/nsdmi-template14.C (v2) David Malcolm
  0 siblings, 1 reply; 40+ messages in thread
From: Jason Merrill @ 2015-12-03 22:17 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On 12/03/2015 04:43 PM, David Malcolm wrote:
> On Thu, 2015-12-03 at 15:33 -0500, Jason Merrill wrote:
>> On 12/03/2015 09:55 AM, David Malcolm wrote:
>>> This patch adds bulletproofing to detect purged tokens, and avoid using
>>> them.
>>>
>>> Alternatively, is it OK to access purged tokens for this kind of thing?
>>> If so, would it make more sense to instead leave their locations untouched
>>> when purging them?
>>
>> I think cp_lexer_previous_token should skip past purged tokens.
>
> Sorry if this is a silly question, but should I limit the iteration e.g.
> by detecting a sentinel value?  e.g.
>    parser->lexer->buffer->address () ?
>
> Or is there guaranteed to be an unpurged token somewhere beforehand?

There should always be an unpurged token.

> Out of interest, the prior tokens here are:
>
> (gdb) p end_tok[0]
> $25 = {type = CPP_GREATER, keyword = RID_MAX, flags = 0 '\000',
> pragma_kind = PRAGMA_NONE, implicit_extern_c = 0,
>    error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value
> = 0x0, value = <tree 0x0>}}
>
> (gdb) p end_tok[-1]
> $26 = {type = CPP_NAME, keyword = RID_MAX, flags = 0 '\000', pragma_kind
> = PRAGMA_NONE, implicit_extern_c = 0,
>    error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value
> = 0x0, value = <tree 0x0>}}
>
> (gdb) p end_tok[-2]
> $27 = {type = CPP_LESS, keyword = RID_MAX, flags = 0 '\000', pragma_kind
> = PRAGMA_NONE, implicit_extern_c = 0,
>    error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value
> = 0x0, value = <tree 0x0>}}
>
> (gdb) p end_tok[-3]
> $28 = {type = 86, keyword = RID_MAX, flags = 1 '\001', pragma_kind =
> PRAGMA_NONE, implicit_extern_c = 0, error_reported = 0,
>    purged_p = 0, location = 202016, u = {tree_check_value =
> 0x7ffff19dfd98, value = <bdver1-direct,bdver1-agu 0x7ffff19dfd98>}}
>
> (gdb) p end_tok[-4]
> $29 = {type = CPP_KEYWORD, keyword = RID_NEW, flags = 1 '\001',
> pragma_kind = PRAGMA_NONE, implicit_extern_c = 0,
>    error_reported = 0, purged_p = 0, location = 201890, u =
> {tree_check_value = 0x7ffff18a8318,
>      value = <identifier_node 0x7ffff18a8318 new>}}
>
> where the previous unpurged token is:
>
> (gdb) p end_tok[-3].purged_p
> $31 = 0
>
> (gdb) call inform (end_tok[-3].location, "")
> ../../src/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C:11:14: note:
>     B* p = new B<N>;
>                ^
>
> which would give a range of:
>
>     B* p = new B<N>;
>            ^~~~~
>
> for the erroneous new expression, rather than:
>
>
>     B* p = new B<N>;
>            ^~~~~~~~
>
> if we used the location of the purged token (the CPP_GREATER).
> I prefer the latter, hence my suggestion about not zero-ing out the
> locations of tokens when purging them.

The unpurged token you're finding is the artificial CPP_TEMPLATE_ID 
token, which seems to need to have its location adjusted to reflect the 
full range of the template-id.

Jason


^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 02/10 v2] Fix g++.dg/cpp0x/nsdmi-template14.C (v2)
  2015-12-03 22:17                     ` Jason Merrill
@ 2015-12-04 14:22                       ` David Malcolm
  0 siblings, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-04 14:22 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

On Thu, 2015-12-03 at 17:17 -0500, Jason Merrill wrote:
> On 12/03/2015 04:43 PM, David Malcolm wrote:
> > On Thu, 2015-12-03 at 15:33 -0500, Jason Merrill wrote:
> >> On 12/03/2015 09:55 AM, David Malcolm wrote:
> >>> This patch adds bulletproofing to detect purged tokens, and avoid using
> >>> them.
> >>>
> >>> Alternatively, is it OK to access purged tokens for this kind of thing?
> >>> If so, would it make more sense to instead leave their locations untouched
> >>> when purging them?
> >>
> >> I think cp_lexer_previous_token should skip past purged tokens.
> >
> > Sorry if this is a silly question, but should I limit the iteration e.g.
> > by detecting a sentinel value?  e.g.
> >    parser->lexer->buffer->address () ?
> >
> > Or is there guaranteed to be an unpurged token somewhere beforehand?
> 
> There should always be an unpurged token.

Thanks.

> > Out of interest, the prior tokens here are:
> >
> > (gdb) p end_tok[0]
> > $25 = {type = CPP_GREATER, keyword = RID_MAX, flags = 0 '\000',
> > pragma_kind = PRAGMA_NONE, implicit_extern_c = 0,
> >    error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value
> > = 0x0, value = <tree 0x0>}}
> >
> > (gdb) p end_tok[-1]
> > $26 = {type = CPP_NAME, keyword = RID_MAX, flags = 0 '\000', pragma_kind
> > = PRAGMA_NONE, implicit_extern_c = 0,
> >    error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value
> > = 0x0, value = <tree 0x0>}}
> >
> > (gdb) p end_tok[-2]
> > $27 = {type = CPP_LESS, keyword = RID_MAX, flags = 0 '\000', pragma_kind
> > = PRAGMA_NONE, implicit_extern_c = 0,
> >    error_reported = 0, purged_p = 1, location = 0, u = {tree_check_value
> > = 0x0, value = <tree 0x0>}}
> >
> > (gdb) p end_tok[-3]
> > $28 = {type = 86, keyword = RID_MAX, flags = 1 '\001', pragma_kind =
> > PRAGMA_NONE, implicit_extern_c = 0, error_reported = 0,
> >    purged_p = 0, location = 202016, u = {tree_check_value =
> > 0x7ffff19dfd98, value = <bdver1-direct,bdver1-agu 0x7ffff19dfd98>}}
> >
> > (gdb) p end_tok[-4]
> > $29 = {type = CPP_KEYWORD, keyword = RID_NEW, flags = 1 '\001',
> > pragma_kind = PRAGMA_NONE, implicit_extern_c = 0,
> >    error_reported = 0, purged_p = 0, location = 201890, u =
> > {tree_check_value = 0x7ffff18a8318,
> >      value = <identifier_node 0x7ffff18a8318 new>}}
> >
> > where the previous unpurged token is:
> >
> > (gdb) p end_tok[-3].purged_p
> > $31 = 0
> >
> > (gdb) call inform (end_tok[-3].location, "")
> > ../../src/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C:11:14: note:
> >     B* p = new B<N>;
> >                ^
> >
> > which would give a range of:
> >
> >     B* p = new B<N>;
> >            ^~~~~
> >
> > for the erroneous new expression, rather than:
> >
> >
> >     B* p = new B<N>;
> >            ^~~~~~~~
> >
> > if we used the location of the purged token (the CPP_GREATER).
> > I prefer the latter, hence my suggestion about not zero-ing out the
> > locations of tokens when purging them.
> 
> The unpurged token you're finding is the artificial CPP_TEMPLATE_ID 
> token, which seems to need to have its location adjusted to reflect the 
> full range of the template-id.

Aha!  Thanks.

Here's an updated fix for g++.dg/cpp0x/nsdmi-template14.C,
which generates meaningful locations/ranges when converting tokens
to CPP_TEMPLATE_ID:

(gdb) call inform (end_tok[-3].location, "")
../../src/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C:11:14: note:
   B* p = new B<N>; // { dg-error "recursive instantiation of non-static data" }
              ^~~~

I added a test case for this to
g++.dg/plugin/diagnostic-test-expressions-1.C.

The updated patch also updates cp_lexer_previous_token to skip
past purged tokens, so that we use the above token when determining
the end of the new-expression, giving:

../../src/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C:11:10: error: recursive instantiation of non-static data member initializer for ‘B<1>::p’
   B* p = new B<N>; // { dg-error "recursive instantiation of non-static data" }
          ^~~~~~~~

and hence we no longer need the "bulletproofing" from the previous
iteration of the patch.

As before, the patch also updates the location of a dg-error directive
in the testcase to reflect improved location information.

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu (in
combination with the other patches from the kit).

gcc/cp/ChangeLog:
	* parser.c (cp_lexer_previous_token): Skip past purged tokens.
	(cp_parser_template_id): When converting a token to
	CPP_TEMPLATE_ID, update the location.

gcc/testsuite/ChangeLog:
	* g++.dg/cpp0x/nsdmi-template14.C: Move dg-error directive.
	* g++.dg/plugin/diagnostic-test-expressions-1.C
	(test_template_id): New function.
---
 gcc/cp/parser.c                                       | 19 +++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C         |  4 ++--
 .../g++.dg/plugin/diagnostic-test-expressions-1.C     |  9 +++++++++
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index d859a89..1e3ada5 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -733,6 +733,13 @@ cp_lexer_previous_token (cp_lexer *lexer)
 {
   cp_token_position tp = cp_lexer_previous_token_position (lexer);
 
+  /* Skip past purged tokens.  */
+  while (tp->purged_p)
+    {
+      gcc_assert (tp != lexer->buffer->address ());
+      tp--;
+    }
+
   return cp_lexer_token_at (lexer, tp);
 }
 
@@ -14733,6 +14740,18 @@ cp_parser_template_id (cp_parser *parser,
 
       /* Reset the contents of the START_OF_ID token.  */
       token->type = CPP_TEMPLATE_ID;
+
+      /* Update the location to be of the form:
+	   template-name < template-argument-list [opt] >
+	   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	 with caret == start at the start of the template-name,
+	 ranging until the closing '>'.  */
+      location_t finish_loc
+	= get_finish (cp_lexer_previous_token (parser->lexer)->location);
+      location_t combined_loc
+	= make_location (token->location, token->location, finish_loc);
+      token->location = combined_loc;
+
       /* Retrieve any deferred checks.  Do not pop this access checks yet
 	 so the memory will not be reclaimed during token replacing below.  */
       token->u.tree_check_value = ggc_cleared_alloc<struct tree_check> ();
diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C
index 9cb01f1..47f5b63 100644
--- a/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C
+++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-template14.C
@@ -8,10 +8,10 @@ template<int> struct A // { dg-error "has been parsed" }
 
 template<int N> struct B
 {
-  B* p = new B<N>;
+  B* p = new B<N>; // { dg-error "recursive instantiation of non-static data" }
 };
 
-B<1> x; // { dg-error "recursive instantiation of non-static data" }
+B<1> x;
 
 struct C
 {
diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
index 826e835..4d3b07c 100644
--- a/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
+++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-expressions-1.C
@@ -697,6 +697,15 @@ public:
   example_template (TYPENAME v);
 };
 
+void test_template_id (void)
+{
+  example_template <int>; /* { dg-warning "declaration does not declare anything" } */
+/* { dg-begin-multiline-output "" }
+   example_template <int>;
+   ^~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
 void test_new (void)
 {
   __emit_expression_range (0, ::new base); /* { dg-warning "range" } */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 07/10] Fix g++.dg/template/ref3.C
  2015-12-03 22:08                   ` David Malcolm
@ 2015-12-04 16:01                     ` Jason Merrill
  2015-12-04 16:45                       ` [PATCH] Add XFAIL to g++.dg/template/ref3.C (PR c++/68699) David Malcolm
  0 siblings, 1 reply; 40+ messages in thread
From: Jason Merrill @ 2015-12-04 16:01 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On 12/03/2015 05:08 PM, David Malcolm wrote:
> On Thu, 2015-12-03 at 15:38 -0500, Jason Merrill wrote:
>> On 12/03/2015 09:55 AM, David Malcolm wrote:
>>> Testcase g++.dg/template/ref3.C:
>>>
>>>        1	// PR c++/28341
>>>        2
>>>        3	template<const int&> struct A {};
>>>        4
>>>        5	template<typename T> struct B
>>>        6	{
>>>        7	  A<(T)0> b; // { dg-error "constant|not a valid" }
>>>        8	  A<T(0)> a; // { dg-error "constant|not a valid" }
>>>        9	};
>>>       10
>>>       11	B<const int&> b;
>>>
>>> The output of this test for both c++11 and c++14 is unaffected
>>> by the patch kit:
>>>    g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
>>>    g++.dg/template/ref3.C:11:15:   required from here
>>>    g++.dg/template/ref3.C:7:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
>>>    g++.dg/template/ref3.C:8:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
>>>
>>> However, the c++98 output is changed:
>>>
>>> Status quo for c++98:
>>> g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
>>> g++.dg/template/ref3.C:11:15:   required from here
>>> g++.dg/template/ref3.C:7:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>>> g++.dg/template/ref3.C:8:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>>>
>>> (line 7 and 8 are at the closing semicolon for fields b and a)
>>>
>>> With the patchkit for c++98:
>>> g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
>>> g++.dg/template/ref3.C:11:15:   required from here
>>> g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>>> g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>>>
>>> So the 2nd:
>>>     "error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression"
>>> moves from line 8 to line 7 (and moves them to earlier, having ranges)
>>>
>>> What's happening is that cp_parser_enclosed_template_argument_list
>>> builds a CAST_EXPR, the first time from cp_parser_cast_expression,
>>> the second time from cp_parser_functional_cast; these have locations
>>> representing the correct respective caret&ranges, i.e.:
>>>
>>>      A<(T)0> b;
>>>        ^~~~
>>>
>>> and:
>>>
>>>      A<T(0)> a;
>>>        ^~~~
>>>
>>> Eventually finish_template_type is called for each, to build a RECORD_TYPE,
>>> and we get a cache hit the 2nd time through here in pt.c:
>>> 8281	      hash = spec_hasher::hash (&elt);
>>> 8282	      entry = type_specializations->find_with_hash (&elt, hash);
>>> 8283
>>> 8284	      if (entry)
>>> 8285		return entry->spec;
>>>
>>> due to:
>>>     template_args_equal (ot=<cast_expr 0x7ffff19bc400>, nt=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/pt.c:7778
>>> which calls:
>>>     cp_tree_equal (t1=<cast_expr 0x7ffff19bc400>, t2=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/tree.c:2833
>>> and returns equality.
>>>
>>> Hence we get a single RECORD_TYPE for the type A<(T)(0)>, and hence
>>> when issuing the errors it uses the TREE_VEC for the first one,
>>> using the location of the first line.
>>
>> Why does the type sharing affect where the parser gives the error?
>
> I believe what's happening is that the patchkit is setting location_t
> values for more expressions than before, including the expression for
> the template param.  pt.c:tsubst_expr has this:
>
>    if (EXPR_HAS_LOCATION (t))
>      input_location = EXPR_LOCATION (t);
>
> I believe that before (in the status quo), the substituted types didn't
> have location_t values, and hence the above conditional didn't fire;
> input_location was coming from a *token* where the expansion happened,
> hence we got an error message on the relevant line for each expansion.
>
> With the patch, the substituted types have location_t values within
> their params, hence the conditional above fires: input_location is
> updated to use the EXPR_LOCATION, which comes from that of the param
> within the type - but with type-sharing it's using the first place where
> the type is created.
>
> Perhaps a better fix is for cp_parser_non_integral_constant_expression
> to take a location_t, rather than have it rely on input_location?

Ah, I see, the error is coming from tsubst_copy_and_build, not 
cp_parser_non_integral_constant_expression.  So indeed this is an effect 
of the canonicalization of template instances, and we aren't going to 
fix it in the context of this patchset.  But this is still a bug, so I'd 
rather have an xfail and a PR than change the expected output.

Jason

>>> I'm not sure what the ideal fix for this is; for now I've worked
>>> around it by updating the dg directives to reflect the new output.
>>>
>>> gcc/testsuite/ChangeLog:
>>> 	* g++.dg/template/ref3.C: Update locations of dg directives.
>>> ---
>>>    gcc/testsuite/g++.dg/template/ref3.C | 6 ++++--
>>>    1 file changed, 4 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/gcc/testsuite/g++.dg/template/ref3.C b/gcc/testsuite/g++.dg/template/ref3.C
>>> index 976c093..6e568c3 100644
>>> --- a/gcc/testsuite/g++.dg/template/ref3.C
>>> +++ b/gcc/testsuite/g++.dg/template/ref3.C
>>> @@ -4,8 +4,10 @@ template<const int&> struct A {};
>>>
>>>    template<typename T> struct B
>>>    {
>>> -  A<(T)0> b; // { dg-error "constant|not a valid" }
>>> -  A<T(0)> a; // { dg-error "constant|not a valid" }
>>> +  A<(T)0> b; // { dg-error "constant" "" { target c++98_only } }
>>> +  // { dg-error "not a valid" "" { target c++11 } 7 }
>>> +
>>> +  A<T(0)> a; // { dg-error "not a valid" "" { target c++11 } }
>>>    };
>>>
>>>    B<const int&> b;
>>>
>>
>
>

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH] Add XFAIL to g++.dg/template/ref3.C (PR c++/68699)
  2015-12-04 16:01                     ` Jason Merrill
@ 2015-12-04 16:45                       ` David Malcolm
  2015-12-04 17:09                         ` Jason Merrill
  0 siblings, 1 reply; 40+ messages in thread
From: David Malcolm @ 2015-12-04 16:45 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, David Malcolm

On Fri, 2015-12-04 at 11:01 -0500, Jason Merrill wrote:
> On 12/03/2015 05:08 PM, David Malcolm wrote:
> > On Thu, 2015-12-03 at 15:38 -0500, Jason Merrill wrote:
> >> On 12/03/2015 09:55 AM, David Malcolm wrote:
> >>> Testcase g++.dg/template/ref3.C:
> >>>
> >>>        1	// PR c++/28341
> >>>        2
> >>>        3	template<const int&> struct A {};
> >>>        4
> >>>        5	template<typename T> struct B
> >>>        6	{
> >>>        7	  A<(T)0> b; // { dg-error "constant|not a valid" }
> >>>        8	  A<T(0)> a; // { dg-error "constant|not a valid" }
> >>>        9	};
> >>>       10
> >>>       11	B<const int&> b;
> >>>
> >>> The output of this test for both c++11 and c++14 is unaffected
> >>> by the patch kit:
> >>>    g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
> >>>    g++.dg/template/ref3.C:11:15:   required from here
> >>>    g++.dg/template/ref3.C:7:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
> >>>    g++.dg/template/ref3.C:8:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
> >>>
> >>> However, the c++98 output is changed:
> >>>
> >>> Status quo for c++98:
> >>> g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
> >>> g++.dg/template/ref3.C:11:15:   required from here
> >>> g++.dg/template/ref3.C:7:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> >>> g++.dg/template/ref3.C:8:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> >>>
> >>> (line 7 and 8 are at the closing semicolon for fields b and a)
> >>>
> >>> With the patchkit for c++98:
> >>> g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
> >>> g++.dg/template/ref3.C:11:15:   required from here
> >>> g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> >>> g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
> >>>
> >>> So the 2nd:
> >>>     "error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression"
> >>> moves from line 8 to line 7 (and moves them to earlier, having ranges)
> >>>
> >>> What's happening is that cp_parser_enclosed_template_argument_list
> >>> builds a CAST_EXPR, the first time from cp_parser_cast_expression,
> >>> the second time from cp_parser_functional_cast; these have locations
> >>> representing the correct respective caret&ranges, i.e.:
> >>>
> >>>      A<(T)0> b;
> >>>        ^~~~
> >>>
> >>> and:
> >>>
> >>>      A<T(0)> a;
> >>>        ^~~~
> >>>
> >>> Eventually finish_template_type is called for each, to build a RECORD_TYPE,
> >>> and we get a cache hit the 2nd time through here in pt.c:
> >>> 8281	      hash = spec_hasher::hash (&elt);
> >>> 8282	      entry = type_specializations->find_with_hash (&elt, hash);
> >>> 8283
> >>> 8284	      if (entry)
> >>> 8285		return entry->spec;
> >>>
> >>> due to:
> >>>     template_args_equal (ot=<cast_expr 0x7ffff19bc400>, nt=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/pt.c:7778
> >>> which calls:
> >>>     cp_tree_equal (t1=<cast_expr 0x7ffff19bc400>, t2=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/tree.c:2833
> >>> and returns equality.
> >>>
> >>> Hence we get a single RECORD_TYPE for the type A<(T)(0)>, and hence
> >>> when issuing the errors it uses the TREE_VEC for the first one,
> >>> using the location of the first line.
> >>
> >> Why does the type sharing affect where the parser gives the error?
> >
> > I believe what's happening is that the patchkit is setting location_t
> > values for more expressions than before, including the expression for
> > the template param.  pt.c:tsubst_expr has this:
> >
> >    if (EXPR_HAS_LOCATION (t))
> >      input_location = EXPR_LOCATION (t);
> >
> > I believe that before (in the status quo), the substituted types didn't
> > have location_t values, and hence the above conditional didn't fire;
> > input_location was coming from a *token* where the expansion happened,
> > hence we got an error message on the relevant line for each expansion.
> >
> > With the patch, the substituted types have location_t values within
> > their params, hence the conditional above fires: input_location is
> > updated to use the EXPR_LOCATION, which comes from that of the param
> > within the type - but with type-sharing it's using the first place where
> > the type is created.
> >
> > Perhaps a better fix is for cp_parser_non_integral_constant_expression
> > to take a location_t, rather than have it rely on input_location?
>
> Ah, I see, the error is coming from tsubst_copy_and_build, not
> cp_parser_non_integral_constant_expression.  So indeed this is an effect
> of the canonicalization of template instances, and we aren't going to
> fix it in the context of this patchset.  But this is still a bug, so I'd
> rather have an xfail and a PR than change the expected output.

Is the following what you had in mind?

gcc/testsuite/ChangeLog:
	* g++.dg/template/ref3.C: Add XFAIL (PR c++/68699).
---
 gcc/testsuite/g++.dg/template/ref3.C | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/gcc/testsuite/g++.dg/template/ref3.C b/gcc/testsuite/g++.dg/template/ref3.C
index 976c093..91e3c93 100644
--- a/gcc/testsuite/g++.dg/template/ref3.C
+++ b/gcc/testsuite/g++.dg/template/ref3.C
@@ -5,7 +5,8 @@ template<const int&> struct A {};
 template<typename T> struct B
 {
   A<(T)0> b; // { dg-error "constant|not a valid" }
-  A<T(0)> a; // { dg-error "constant|not a valid" }
+  A<T(0)> a; // { dg-error "constant|not a valid" "" { xfail c++98_only } }
+                                                       // PR c++/68699
 };
 
 B<const int&> b;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH] Add XFAIL to g++.dg/template/ref3.C (PR c++/68699)
  2015-12-04 16:45                       ` [PATCH] Add XFAIL to g++.dg/template/ref3.C (PR c++/68699) David Malcolm
@ 2015-12-04 17:09                         ` Jason Merrill
  0 siblings, 0 replies; 40+ messages in thread
From: Jason Merrill @ 2015-12-04 17:09 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches

On 12/04/2015 11:45 AM, David Malcolm wrote:
> On Fri, 2015-12-04 at 11:01 -0500, Jason Merrill wrote:
>> On 12/03/2015 05:08 PM, David Malcolm wrote:
>>> On Thu, 2015-12-03 at 15:38 -0500, Jason Merrill wrote:
>>>> On 12/03/2015 09:55 AM, David Malcolm wrote:
>>>>> Testcase g++.dg/template/ref3.C:
>>>>>
>>>>>         1	// PR c++/28341
>>>>>         2
>>>>>         3	template<const int&> struct A {};
>>>>>         4
>>>>>         5	template<typename T> struct B
>>>>>         6	{
>>>>>         7	  A<(T)0> b; // { dg-error "constant|not a valid" }
>>>>>         8	  A<T(0)> a; // { dg-error "constant|not a valid" }
>>>>>         9	};
>>>>>        10
>>>>>        11	B<const int&> b;
>>>>>
>>>>> The output of this test for both c++11 and c++14 is unaffected
>>>>> by the patch kit:
>>>>>     g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
>>>>>     g++.dg/template/ref3.C:11:15:   required from here
>>>>>     g++.dg/template/ref3.C:7:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
>>>>>     g++.dg/template/ref3.C:8:11: error: '0' is not a valid template argument for type 'const int&' because it is not an lvalue
>>>>>
>>>>> However, the c++98 output is changed:
>>>>>
>>>>> Status quo for c++98:
>>>>> g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
>>>>> g++.dg/template/ref3.C:11:15:   required from here
>>>>> g++.dg/template/ref3.C:7:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>>>>> g++.dg/template/ref3.C:8:11: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>>>>>
>>>>> (line 7 and 8 are at the closing semicolon for fields b and a)
>>>>>
>>>>> With the patchkit for c++98:
>>>>> g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>':
>>>>> g++.dg/template/ref3.C:11:15:   required from here
>>>>> g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>>>>> g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
>>>>>
>>>>> So the 2nd:
>>>>>      "error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression"
>>>>> moves from line 8 to line 7 (and moves them to earlier, having ranges)
>>>>>
>>>>> What's happening is that cp_parser_enclosed_template_argument_list
>>>>> builds a CAST_EXPR, the first time from cp_parser_cast_expression,
>>>>> the second time from cp_parser_functional_cast; these have locations
>>>>> representing the correct respective caret&ranges, i.e.:
>>>>>
>>>>>       A<(T)0> b;
>>>>>         ^~~~
>>>>>
>>>>> and:
>>>>>
>>>>>       A<T(0)> a;
>>>>>         ^~~~
>>>>>
>>>>> Eventually finish_template_type is called for each, to build a RECORD_TYPE,
>>>>> and we get a cache hit the 2nd time through here in pt.c:
>>>>> 8281	      hash = spec_hasher::hash (&elt);
>>>>> 8282	      entry = type_specializations->find_with_hash (&elt, hash);
>>>>> 8283
>>>>> 8284	      if (entry)
>>>>> 8285		return entry->spec;
>>>>>
>>>>> due to:
>>>>>      template_args_equal (ot=<cast_expr 0x7ffff19bc400>, nt=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/pt.c:7778
>>>>> which calls:
>>>>>      cp_tree_equal (t1=<cast_expr 0x7ffff19bc400>, t2=<cast_expr 0x7ffff19bc480>) at ../../src/gcc/cp/tree.c:2833
>>>>> and returns equality.
>>>>>
>>>>> Hence we get a single RECORD_TYPE for the type A<(T)(0)>, and hence
>>>>> when issuing the errors it uses the TREE_VEC for the first one,
>>>>> using the location of the first line.
>>>>
>>>> Why does the type sharing affect where the parser gives the error?
>>>
>>> I believe what's happening is that the patchkit is setting location_t
>>> values for more expressions than before, including the expression for
>>> the template param.  pt.c:tsubst_expr has this:
>>>
>>>     if (EXPR_HAS_LOCATION (t))
>>>       input_location = EXPR_LOCATION (t);
>>>
>>> I believe that before (in the status quo), the substituted types didn't
>>> have location_t values, and hence the above conditional didn't fire;
>>> input_location was coming from a *token* where the expansion happened,
>>> hence we got an error message on the relevant line for each expansion.
>>>
>>> With the patch, the substituted types have location_t values within
>>> their params, hence the conditional above fires: input_location is
>>> updated to use the EXPR_LOCATION, which comes from that of the param
>>> within the type - but with type-sharing it's using the first place where
>>> the type is created.
>>>
>>> Perhaps a better fix is for cp_parser_non_integral_constant_expression
>>> to take a location_t, rather than have it rely on input_location?
>>
>> Ah, I see, the error is coming from tsubst_copy_and_build, not
>> cp_parser_non_integral_constant_expression.  So indeed this is an effect
>> of the canonicalization of template instances, and we aren't going to
>> fix it in the context of this patchset.  But this is still a bug, so I'd
>> rather have an xfail and a PR than change the expected output.
>
> Is the following what you had in mind?

Yes, thanks.

Jason


^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 01/10] C++ FE: expression ranges v4
  2015-12-03 14:37               ` [PATCH 01/10] C++ FE: expression ranges v4 David Malcolm
@ 2015-12-04 17:10                 ` Jason Merrill
  2015-12-04 18:13                   ` David Malcolm
  0 siblings, 1 reply; 40+ messages in thread
From: Jason Merrill @ 2015-12-04 17:10 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches, Manuel López-Ibáñez

On 12/03/2015 09:55 AM, David Malcolm wrote:
> @@ -362,10 +362,11 @@ convert_to_real_1 (tree type, tree expr, bool fold_p)
>       case REAL_TYPE:
>         /* Ignore the conversion if we don't need to store intermediate
>   	 results and neither type is a decimal float.  */
> -      return build1 ((flag_float_store
> -		     || DECIMAL_FLOAT_TYPE_P (type)
> -		     || DECIMAL_FLOAT_TYPE_P (itype))
> -		     ? CONVERT_EXPR : NOP_EXPR, type, expr);
> +      return build1_loc (loc,
> +			 (flag_float_store
> +			  || DECIMAL_FLOAT_TYPE_P (type)
> +			  || DECIMAL_FLOAT_TYPE_P (itype))
> +			 ? CONVERT_EXPR : NOP_EXPR, type, expr);
....
> @@ -5438,7 +5438,7 @@ build_nop (tree type, tree expr)
>  {
>    if (type == error_mark_node || error_operand_p (expr))
>      return expr;
> -  return build1 (NOP_EXPR, type, expr);
> +  return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr);

Hmm, I'm uneasy about assigning a location to a conversion or other 
expression that doesn't correspond to particular text; it could be 
associated with the location of the operand or the enclosing expression 
that prompted the conversion.  I think we've been deliberately leaving 
the location unset.  But that causes problems with code that only looks 
at the top-level EXPR_LOCATION.  Arguably such code should be fixed to 
look at the pre-conversion expression tree for a location, but I guess 
this is reasonable.

Past GCC 6 I think we definitely want to use a new tree code rather than 
cp_expr; as Jakub pointed out, cp_expr doesn't do anything for templates 
or language-independent code.

The current patchset is OK for GCC 6.

Jason

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 01/10] C++ FE: expression ranges v4
  2015-12-04 17:10                 ` Jason Merrill
@ 2015-12-04 18:13                   ` David Malcolm
  0 siblings, 0 replies; 40+ messages in thread
From: David Malcolm @ 2015-12-04 18:13 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Manuel López-Ibáñez

On Fri, 2015-12-04 at 12:10 -0500, Jason Merrill wrote:
> On 12/03/2015 09:55 AM, David Malcolm wrote:
> > @@ -362,10 +362,11 @@ convert_to_real_1 (tree type, tree expr, bool fold_p)
> >       case REAL_TYPE:
> >         /* Ignore the conversion if we don't need to store intermediate
> >   	 results and neither type is a decimal float.  */
> > -      return build1 ((flag_float_store
> > -		     || DECIMAL_FLOAT_TYPE_P (type)
> > -		     || DECIMAL_FLOAT_TYPE_P (itype))
> > -		     ? CONVERT_EXPR : NOP_EXPR, type, expr);
> > +      return build1_loc (loc,
> > +			 (flag_float_store
> > +			  || DECIMAL_FLOAT_TYPE_P (type)
> > +			  || DECIMAL_FLOAT_TYPE_P (itype))
> > +			 ? CONVERT_EXPR : NOP_EXPR, type, expr);
> ....
> > @@ -5438,7 +5438,7 @@ build_nop (tree type, tree expr)
> >  {
> >    if (type == error_mark_node || error_operand_p (expr))
> >      return expr;
> > -  return build1 (NOP_EXPR, type, expr);
> > +  return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr);
> 
> Hmm, I'm uneasy about assigning a location to a conversion or other 
> expression that doesn't correspond to particular text; it could be 
> associated with the location of the operand or the enclosing expression 
> that prompted the conversion.  I think we've been deliberately leaving 
> the location unset.  But that causes problems with code that only looks 
> at the top-level EXPR_LOCATION.  Arguably such code should be fixed to 
> look at the pre-conversion expression tree for a location, but I guess 
> this is reasonable.

> Past GCC 6 I think we definitely want to use a new tree code rather than 
> cp_expr; as Jakub pointed out, cp_expr doesn't do anything for templates 
> or language-independent code.
> 
> The current patchset is OK for GCC 6.

Thanks.  I've committed it to trunk as r231293.


^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2015-12-04 18:13 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-07  3:40 [PATCH/RFC] C++ FE: expression ranges (work in progress) David Malcolm
2015-11-15  4:43 ` [PATCH/RFC] C++ FE: expression ranges (v2) David Malcolm
2015-11-19 20:46   ` Jason Merrill
2015-11-21  8:22     ` Jason Merrill
2015-11-21  8:22       ` Jakub Jelinek
2015-11-23 10:02         ` Richard Biener
2015-11-23 16:58           ` David Malcolm
2015-11-23 17:08             ` Jakub Jelinek
2015-11-23 17:09               ` Marek Polacek
2015-11-23 19:45                 ` Jason Merrill
2015-11-24  9:42                   ` Richard Biener
2015-11-24 11:08                     ` David Malcolm
2015-11-24 11:50                       ` Richard Biener
2015-11-24 12:15                       ` Marek Polacek
2015-11-25 20:32       ` [PATCH/RFC 0/2] C++ FE: expression ranges (v3) David Malcolm
2015-11-25 20:32         ` [PATCH 1/2] RFC: C++: attempt to provide location_t in more places David Malcolm
2015-11-25 21:33           ` Jason Merrill
2015-12-03 14:36             ` [PATCH 00/10] C++ expression ranges v4 David Malcolm
2015-12-03 14:36               ` [PATCH 04/10] Fix g++.dg/template/crash55.C David Malcolm
2015-12-03 14:37               ` [PATCH 10/10] Fix g++.dg/warn/Wconversion-real-integer2.C David Malcolm
2015-12-03 14:37               ` [PATCH 01/10] C++ FE: expression ranges v4 David Malcolm
2015-12-04 17:10                 ` Jason Merrill
2015-12-04 18:13                   ` David Malcolm
2015-12-03 14:37               ` [PATCH 07/10] Fix g++.dg/template/ref3.C David Malcolm
2015-12-03 20:38                 ` Jason Merrill
2015-12-03 22:08                   ` David Malcolm
2015-12-04 16:01                     ` Jason Merrill
2015-12-04 16:45                       ` [PATCH] Add XFAIL to g++.dg/template/ref3.C (PR c++/68699) David Malcolm
2015-12-04 17:09                         ` Jason Merrill
2015-12-03 14:37               ` [PATCH 02/10] Fix g++.dg/cpp0x/nsdmi-template14.C David Malcolm
2015-12-03 20:33                 ` Jason Merrill
2015-12-03 21:43                   ` David Malcolm
2015-12-03 22:17                     ` Jason Merrill
2015-12-04 14:22                       ` [PATCH 02/10 v2] Fix g++.dg/cpp0x/nsdmi-template14.C (v2) David Malcolm
2015-12-03 14:37               ` [PATCH 03/10] Fix g++.dg/gomp/loop-1.C David Malcolm
2015-12-03 14:37               ` [PATCH 08/10] Fix g++.dg/ubsan/pr63956.C David Malcolm
2015-12-03 14:37               ` [PATCH 05/10] Fix location of dg-error within g++.dg/template/pr64100.C David Malcolm
2015-12-03 14:37               ` [PATCH 09/10] Fix g++.dg/warn/pr35635.C David Malcolm
2015-12-03 14:53               ` [PATCH 06/10] Fix g++.dg/template/pseudodtor3.C David Malcolm
2015-11-25 20:38         ` [PATCH 2/2] RFC: C++ FE: expression ranges (work in progress) v3 David Malcolm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).