[2/6] invoke_xmethod & array_view

Message ID 8947e861-0974-f6b4-f5a8-a181df210e08@redhat.com
State New, archived
Headers

Commit Message

Pedro Alves Nov. 21, 2018, 12:46 p.m. UTC
  On 10/17/2018 05:21 PM, Simon Marchi wrote:
> On 2018-10-15 11:11 a.m., Pedro Alves wrote:
>> This replaces more pointer+length with gdb::array_view.  This time,
>> around invoke_xmethod, and then propagating the fallout around, which
>> inevitably leads to the overload resolution code.
>>
>> There are several places in the code that want to grab a slice of an
>> array, by advanting the array pointer, and decreasing the length
> 
> "advanting"

Fixed.

> 
>> pointer.  This patch introduces a pair of new
>> gdb::array_view::slice(...) methods to make that convenient and clear.
>> Unit test included.
> 
> Cool!
> 
> Just some minor comments.
> 
> I noticed some lines > 80 columns:
> 
> $ git show | grep ^\+ | tr '\t' '        ' | egrep '.{81}'
> +      return call_xmethod (argvec[0], gdb::make_array_view (argvec + 1, nargs));
> +value_user_defined_op (struct value **argp, gdb::array_view<value *> args, char *name,
> +      && classify_oload_match (new_oload_champ_bv, args.size (), 0) == STANDARD)
> 

Fixed the middle one, thanks.  The others are actually at 80 cols.

> 
>> diff --git a/gdb/common/array-view.h b/gdb/common/array-view.h
>> index 45d1a4720e..679d2e95c7 100644
>> --- a/gdb/common/array-view.h
>> +++ b/gdb/common/array-view.h
>> @@ -169,6 +169,17 @@ public:
>>    constexpr size_type size () const noexcept { return m_size; }
>>    constexpr bool empty () const noexcept { return m_size == 0; }
>>  
>> +  /* Slice an array view.  */
>> +
>> +  /* Return a new array view over SIZE elements starting at START.  */
>> +  constexpr array_view<T> slice (size_type start, size_t size) const noexcept
>> +  { return {m_array + start, size}; }
> 
> I'm sure there's a logic for using size_type for one parameter and size_t for the other
> (instead of size_type for both), but what is it?

There's no logic, just some copy&pasting&editing, I guess.

> 
>> +
>> +  /* Return a new array view over all the elements after START,
>> +     inclusive.  */
>> +  constexpr array_view<T> slice (size_type start) const noexcept
>> +  { return {m_array + start, size () - start}; }
> 
> It would perhaps be good to have some asserts (that are only there in development
> mode, maybe) to make sure we don't do stupid things, like take a slice
> past the end of the array, things like that.  A bit like those asserts enabled by
> __GLIBCXX_DEBUG.

Doing it in development mode might be useful.  In that case, it would
better be done to e.g., operator[] too, I think.  Also, not sure we
already have a development vs release preprocessor macro?
Thus I'd rather see it in a separate patch.

> 
>> diff --git a/gdb/extension.h b/gdb/extension.h
>> index 0c8c4ee934..4716d6f360 100644
>> --- a/gdb/extension.h
>> +++ b/gdb/extension.h
>> @@ -22,6 +22,7 @@
>>  
>>  #include "mi/mi-cmds.h" /* For PRINT_NO_VALUES, etc.  */
>>  #include "common/vec.h"
>> +#include "common/array-view.h"
>>  
>>  struct breakpoint;
>>  struct command_line;
>> @@ -186,38 +187,35 @@ struct xmethod_worker
>>    virtual ~xmethod_worker () = default;
>>  
>>    /* Invoke the xmethod encapsulated in this worker and return the result.
>> -     The method is invoked on OBJ with arguments in the ARGS array.  NARGS is
>> -     the length of the this array.  */
>> +     The method is invoked on OBJ with arguments in the ARGS array.  */
>>  
>> -  virtual value *invoke (value *obj, value **args, int nargs) = 0;
>> +  virtual value *invoke (value *obj, gdb::array_view<value *> args) = 0;
>>  
>>    /* Return the arg types of the xmethod encapsulated in this worker.
>> -     An array of arg types is returned.  The length of the array is returned in
>> -     NARGS.  The type of the 'this' object is returned as the first element of
>> -     array.  */
>> +     The type of the 'this' object is returned as the first element of
>> +     the vector.  */
>>  
>> -  type **get_arg_types (int *nargs);
>> +  std::vector<type *> get_arg_types ();
>>  
>>    /* Return the type of the result of the xmethod encapsulated in this worker.
>> -     OBJECT, ARGS, NARGS are the same as for invoke.  */
>> +     OBJECT and ARGS are the same as for invoke.  */
>>  
>> -  type *get_result_type (value *object, value **args, int nargs);
>> +  type *get_result_type (value *object, gdb::array_view<value *> args);
>>  
>>  private:
>>  
>> -  /* Return the types of the arguments the method takes.  The number of
>> -     arguments is returned in NARGS, and their types are returned in the array
>> -     ARGTYPES.  */
>> +  /* Return the types of the arguments the method takes.  The types
>> +     are returned in TYPE_ARGS, one per argument.  */
>>  
>>    virtual enum ext_lang_rc do_get_arg_types
>> -    (int *nargs, struct type ***arg_types) = 0;
>> +    (std::vector<type *> &type_args) = 0;
> 
> Could you change this to be a pointer to the std::vector?
> 
> https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards#Avoid_non-const_reference_parameters.2C_use_pointers_instead
> 

I changed it, though I'm not a big fan of that rule.  

As I wrote at <https://gcc.gnu.org/ml/gcc/2018-07/msg00190.html>, I used to
prefer avoiding non-const reference output parameters too back in my
earlier C++ days, but I no longer do so much nowadays.

The main point of avoiding non-const reference parameters is that
supposedly they make function callers unclearer, with no
visual indication that the function modified the argument.  The
idea is that "&foo" instead of "foo" is that visual distinction.
But, that point can easily become moot because the visual distinction
can be easily lost with pointers too:

 // the usual argument is that using pointers for output parameters shows
 // clearly that bar is going to be modified:
 function (foo, &bar);

 // but well, we often works with pointers, and if "bar" is a pointer,
 // we don't get any visual clue anyway either:
 function (foo, bar);

 // which suggests that what really helps is seeing the output
 // variable nearby, suggesting to define it right before the
 // function call that fills it in, and I would go as far
 // as saying that clearer symbol names help even more.  For e.g.:
 B bar_out;
 fill_bar (foo, bar_out);

I think that symbol and function naming above is much more important
than "&bar" vs "bar".  A function called "fill_bar" is clearly going
to write to its "bar_out" argument.

Also, a pointer can be null, while a reference can not.  So
a reference parameter automatically implies that you can't pass
a NULL pointer to the function (which makes the function's
implementation a little bit clearer too), while with a pointer parameter
you have to document that, and maybe assert it.  With a reference, the
compiler is free to optimize accordingly (assume non-null), while with
a pointer, you have to use gcc's attribute nonnull if you want that,
which no one does.

Also, for std::vector parameters in particular, passing by pointer
leads to uglier code in the function's implementation, like e.g.,

 (*vec)[idx] = 0;

instead of:

 vec[idx] = 0;

We end up with a few instances like that in the series, though admittedly
not that many, otherwise I think I'd add something like:

  auto &vec = *vec_param;

at the top of the function and then use vec throughout.

So in sum, I nowadays tend to look at reference vs parameter more from
the "pointer: optional, can be NULL", vs "reference: non-optional" angle.
Though, given GDB's history, we definetely use pointers pervasively
in function parameters even when they're not supposed to be optional,
that's for sure.

Maybe we could come up with some middle ground rule, like always
putting in-out and output parameters last, and stressing to use
clear symbol names.

Anyway, I don't want to block on that discussion (my 1 month round trip
time not helping! :-D), so I did the change.

>> @@ -312,19 +312,19 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *oper,
>>     function, otherwise return NULL.  */
>>  
>>  static struct value *
>> -value_user_defined_op (struct value **argp, struct value **args, char *name,
>> -                       int *static_memfuncp, int nargs, enum noside noside)
>> +value_user_defined_op (struct value **argp, gdb::array_view<value *> args, char *name,
>> +		       int *static_memfuncp, enum noside noside)
>>  {
>>    struct value *result = NULL;
>>  
>>    if (current_language->la_language == language_cplus)
>>      {
>> -      result = value_user_defined_cpp_op (args, nargs, name, static_memfuncp,
>> +      result = value_user_defined_cpp_op (args, name, static_memfuncp,
>>  					  noside);
>>      }
> 
> Maybe remove the extra braces, while touching this.

This is from <https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards#Whitespaces>:

 "Any two or more lines in code should be wrapped in braces, even if they are comments, as they look like separate statements: "


Here's what I merged.

From 6b1747cd135ff9859fceb6043179b1ef94363996 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 21 Nov 2018 11:55:12 +0000
Subject: [PATCH 2/6] invoke_xmethod & array_view

This replaces more pointer+length with gdb::array_view.  This time,
around invoke_xmethod, and then propagating the fallout around, which
inevitably leaks to the overload resolution code.

There are several places in the code that want to grab a slice of an
array, by advancing the array pointer, and decreasing the length
pointer.  This patch introduces a pair of new
gdb::array_view::slice(...) methods to make that convenient and clear.
Unit test included.

gdb/ChangeLog:
2018-11-21  Pedro Alves  <palves@redhat.com>

	* common/array-view.h (array_view::splice(size_type, size_t)): New.
	(array_view::splice(size_type)): New.
	* eval.c (eval_call, evaluate_funcall): Adjust to use array_view.
	* extension.c (xmethod_worker::get_arg_types): Adjust to return an
	std::vector.
	(xmethod_worker::get_result_type): Adjust to use gdb::array_view.
	* extension.h: Include "common/array-view.h".
	(xmethod_worker::invoke): Adjust to use gdb::array_view.
	(xmethod_worker::get_arg_types): Adjust to return an std::vector.
	(xmethod_worker::get_result_type): Adjust to use gdb::array_view.
	(xmethod_worker::do_get_arg_types): Adjust to use std::vector.
	(xmethod_worker::do_get_result_type): Adjust to use
	gdb::array_view.
	* gdbtypes.c (rank_function): Adjust to use gdb::array_view.
	* gdbtypes.h: Include "common/array-view.h".
	(rank_function): Adjust to use gdb::array_view.
	* python/py-xmethods.c (python_xmethod_worker::invoke)
	(python_xmethod_worker::do_get_arg_types)
	(python_xmethod_worker::do_get_result_type)
	(python_xmethod_worker::invoke): Adjust to new interfaces.
	* valarith.c (value_user_defined_cpp_op, value_user_defined_op)
	(value_x_binop, value_x_unop): Adjust to use gdb::array_view.
	* valops.c (find_overload_match, find_oload_champ_namespace)
	(find_oload_champ_namespace_loop, find_oload_champ): Adjust to use
	gdb:array_view and the new xmethod_worker interfaces.
	* value.c (result_type_of_xmethod, call_xmethod): Adjust to use
	gdb::array_view.
	* value.h (find_overload_match, result_type_of_xmethod)
	(call_xmethod): Adjust to use gdb::array_view.
	* unittests/array-view-selftests.c: Add slicing tests.
---
 gdb/ChangeLog                        |  33 +++++++++++
 gdb/common/array-view.h              |  11 ++++
 gdb/eval.c                           |  14 +++--
 gdb/extension.c                      |  12 ++--
 gdb/extension.h                      |  32 +++++------
 gdb/gdbtypes.c                       |  21 ++++---
 gdb/gdbtypes.h                       |   5 +-
 gdb/python/py-xmethods.c             |  40 ++++++--------
 gdb/unittests/array-view-selftests.c |  22 ++++++++
 gdb/valarith.c                       |  50 ++++++++---------
 gdb/valops.c                         | 104 +++++++++++++++++------------------
 gdb/value.c                          |  13 ++---
 gdb/value.h                          |   6 +-
 13 files changed, 212 insertions(+), 151 deletions(-)
  

Comments

Simon Marchi Nov. 26, 2018, 5:18 p.m. UTC | #1
On 2018-11-21 07:46, Pedro Alves wrote:
> I changed it, though I'm not a big fan of that rule.
> 
> As I wrote at <https://gcc.gnu.org/ml/gcc/2018-07/msg00190.html>, I 
> used to
> prefer avoiding non-const reference output parameters too back in my
> earlier C++ days, but I no longer do so much nowadays.
> 
> The main point of avoiding non-const reference parameters is that
> supposedly they make function callers unclearer, with no
> visual indication that the function modified the argument.  The
> idea is that "&foo" instead of "foo" is that visual distinction.
> But, that point can easily become moot because the visual distinction
> can be easily lost with pointers too:
> 
>  // the usual argument is that using pointers for output parameters 
> shows
>  // clearly that bar is going to be modified:
>  function (foo, &bar);
> 
>  // but well, we often works with pointers, and if "bar" is a pointer,
>  // we don't get any visual clue anyway either:
>  function (foo, bar);
> 
>  // which suggests that what really helps is seeing the output
>  // variable nearby, suggesting to define it right before the
>  // function call that fills it in, and I would go as far
>  // as saying that clearer symbol names help even more.  For e.g.:
>  B bar_out;
>  fill_bar (foo, bar_out);

Well, I think that doing both (passing a pointer to the function, and 
defining the variable as close as possible) helps, they are not mutually 
exclusive.  As for the name, it depends on the situation...  if you are 
then going to pass bar_out as input to another function, then it's 
misleading.

I think what helps the most is the fact that arguments passed by 
non-const references are highlighted red in my IDE :)

> I think that symbol and function naming above is much more important
> than "&bar" vs "bar".  A function called "fill_bar" is clearly going
> to write to its "bar_out" argument.

As mentioned above, an "out" argument passed to a function call may be 
an "in" argument to the next one.  I agree that good naming is 
important, but there is no one trick that will make the code "100%" 
readable.  They all help a little bit towards that goal.

> Also, a pointer can be null, while a reference can not.  So
> a reference parameter automatically implies that you can't pass
> a NULL pointer to the function (which makes the function's
> implementation a little bit clearer too), while with a pointer 
> parameter
> you have to document that, and maybe assert it. With a reference, the
> compiler is free to optimize accordingly (assume non-null), while with
> a pointer, you have to use gcc's attribute nonnull if you want that,
> which no one does.
> 
> Also, for std::vector parameters in particular, passing by pointer
> leads to uglier code in the function's implementation, like e.g.,
> 
>  (*vec)[idx] = 0;
> 
> instead of:
> 
>  vec[idx] = 0;
> 
> We end up with a few instances like that in the series, though 
> admittedly
> not that many, otherwise I think I'd add something like:
> 
>   auto &vec = *vec_param;
> 
> at the top of the function and then use vec throughout.

I acknowledge all these downsides.  My opinion (as of today) is that 
they are reasonable trade-offs.  I'm in the camp of "code is written 
once, read many times".  I think that when writing it, it's not 
difficult to look up what the function you are calling expects (can a 
pointer be null).  But when reading a function, if the little & sign can 
save a round trip to the called function's doc, I think it's worth it.

> So in sum, I nowadays tend to look at reference vs parameter more from
> the "pointer: optional, can be NULL", vs "reference: non-optional" 
> angle.
> Though, given GDB's history, we definetely use pointers pervasively
> in function parameters even when they're not supposed to be optional,
> that's for sure.

For "in" parameters, I think it's a no-brainer to do that.

> Maybe we could come up with some middle ground rule, like always
> putting in-out and output parameters last, and stressing to use
> clear symbol names.

Hmmm maybe, I would have to see it in practice to judge how feasible it 
is.

> Anyway, I don't want to block on that discussion (my 1 month round trip
> time not helping! :-D), so I did the change.

Thanks!  Note that I have introduced this rule kind of unilaterally 
after having no response when I proposed it:

https://sourceware.org/ml/gdb-patches/2018-03/msg00145.html
https://sourceware.org/ml/gdb-patches/2018-05/msg00450.html

I thought it was reasonable to do so because it's not a huge commitment 
and easily reversible if people ended up disagreeing.  So it's not as if 
it's set in stone.

Simon
  
Pedro Alves Nov. 26, 2018, 5:54 p.m. UTC | #2
On 11/26/2018 05:18 PM, Simon Marchi wrote:

> I acknowledge all these downsides.  My opinion (as of today) is that they are reasonable trade-offs.  I'm in the camp of "code is written once, read many times".  I think that when writing it, it's not difficult to look up what the function you are calling expects (can a pointer be null).  But when reading a function, if the little & sign can save a round trip to the called function's doc, I think it's worth it.

In the patch series in question, do you think it helps?  IMHO it
doesn't.  Passing a vector by copy would be so strange that I'd
never think that that is what is going on.  Which leaves non
or non-const references.  And then if one has to look up
the function description to know the function is adding to the vector,
then that means that one doesn't really have a clue what the function
is doing, IMO.  Once you know, then you just know and the & doesn't
really help.

John's series is also passing output vectors by non-const reference:

 https://sourceware.org/ml/gdb-patches/2018-11/msg00171.html

and that code looks pretty clear to me as is.

Maybe it's particular to vectors/containers, not sure.  Maybe
a prototype like:
  void foo (int i, int &bar, float f, S *);
would be more confusing to me.  Can't say either, I guess because
I'm not seeing myself writing a function with such a prototype.

> 
>> So in sum, I nowadays tend to look at reference vs parameter more from
>> the "pointer: optional, can be NULL", vs "reference: non-optional" angle.
>> Though, given GDB's history, we definetely use pointers pervasively
>> in function parameters even when they're not supposed to be optional,
>> that's for sure.
> 
> For "in" parameters, I think it's a no-brainer to do that.
> 
>> Maybe we could come up with some middle ground rule, like always
>> putting in-out and output parameters last, and stressing to use
>> clear symbol names.
> 
> Hmmm maybe, I would have to see it in practice to judge how feasible it is.
>> Anyway, I don't want to block on that discussion (my 1 month round trip
>> time not helping! :-D), so I did the change.
> 
> Thanks!  Note that I have introduced this rule kind of unilaterally after having no response when I proposed it:
> 
> https://sourceware.org/ml/gdb-patches/2018-03/msg00145.html
> https://sourceware.org/ml/gdb-patches/2018-05/msg00450.html
> 
> I thought it was reasonable to do so because it's not a huge commitment and easily reversible if people ended up disagreeing.  So it's not as if it's set in stone.
Thanks,
Pedro Alves
  
Simon Marchi Nov. 26, 2018, 6:28 p.m. UTC | #3
On 2018-11-26 12:54, Pedro Alves wrote:
> On 11/26/2018 05:18 PM, Simon Marchi wrote:
> 
>> I acknowledge all these downsides.  My opinion (as of today) is that 
>> they are reasonable trade-offs.  I'm in the camp of "code is written 
>> once, read many times".  I think that when writing it, it's not 
>> difficult to look up what the function you are calling expects (can a 
>> pointer be null).  But when reading a function, if the little & sign 
>> can save a round trip to the called function's doc, I think it's worth 
>> it.
> 
> In the patch series in question, do you think it helps?  IMHO it
> doesn't.  Passing a vector by copy would be so strange that I'd
> never think that that is what is going on.  Which leaves non
> or non-const references.  And then if one has to look up
> the function description to know the function is adding to the vector,
> then that means that one doesn't really have a clue what the function
> is doing, IMO.  Once you know, then you just know and the & doesn't
> really help.

It's hard to tell now because as you say, once I know, I know.  And I 
happen to know now :).  Also, this is a case of

    did_it_work = get_stuff (stuff);

So in this case, I admit it would be clear either way.

> 
> John's series is also passing output vectors by non-const reference:
> 
>  https://sourceware.org/ml/gdb-patches/2018-11/msg00171.html
> 
> and that code looks pretty clear to me as is.

There, it kinda helps that the variable is named "result":

   get_syscalls_by_group (gdbarch, group_name, result)

If the result of get_syscalls_by_groups was used for something else than 
being returned, it probably wouldn't have been named "result".  But even 
with that name, I think the & adds a little something that makes it 
obvious at the first eyesight, even before parsing the text:

   get_syscalls_by_group (gdbarch, group_name, &result)

I'm not saying that it's a major impediment to reading the code, just 
one little thing that contribute to readability (IMO).  Again, if people 
believe this rule is more an annoyance than anything else, we can just 
remove it.

Simon
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index e315f34dc1..27da6431b3 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,36 @@ 
+2018-11-21  Pedro Alves  <palves@redhat.com>
+
+	* common/array-view.h (array_view::splice(size_type, size_t)): New.
+	(array_view::splice(size_type)): New.
+	* eval.c (eval_call, evaluate_funcall): Adjust to use array_view.
+	* extension.c (xmethod_worker::get_arg_types): Adjust to return an
+	std::vector.
+	(xmethod_worker::get_result_type): Adjust to use gdb::array_view.
+	* extension.h: Include "common/array-view.h".
+	(xmethod_worker::invoke): Adjust to use gdb::array_view.
+	(xmethod_worker::get_arg_types): Adjust to return an std::vector.
+	(xmethod_worker::get_result_type): Adjust to use gdb::array_view.
+	(xmethod_worker::do_get_arg_types): Adjust to use std::vector.
+	(xmethod_worker::do_get_result_type): Adjust to use
+	gdb::array_view.
+	* gdbtypes.c (rank_function): Adjust to use gdb::array_view.
+	* gdbtypes.h: Include "common/array-view.h".
+	(rank_function): Adjust to use gdb::array_view.
+	* python/py-xmethods.c (python_xmethod_worker::invoke)
+	(python_xmethod_worker::do_get_arg_types)
+	(python_xmethod_worker::do_get_result_type)
+	(python_xmethod_worker::invoke): Adjust to new interfaces.
+	* valarith.c (value_user_defined_cpp_op, value_user_defined_op)
+	(value_x_binop, value_x_unop): Adjust to use gdb::array_view.
+	* valops.c (find_overload_match, find_oload_champ_namespace)
+	(find_oload_champ_namespace_loop, find_oload_champ): Adjust to use
+	gdb:array_view and the new xmethod_worker interfaces.
+	* value.c (result_type_of_xmethod, call_xmethod): Adjust to use
+	gdb::array_view.
+	* value.h (find_overload_match, result_type_of_xmethod)
+	(call_xmethod): Adjust to use gdb::array_view.
+	* unittests/array-view-selftests.c: Add slicing tests.
+
 2018-11-21  Pedro Alves  <palves@redhat.com>
 
 	* ada-lang.c (ada_evaluate_subexp): Adjust to pass an array_view.
diff --git a/gdb/common/array-view.h b/gdb/common/array-view.h
index 9c5fa2a971..d7293c7c5f 100644
--- a/gdb/common/array-view.h
+++ b/gdb/common/array-view.h
@@ -169,6 +169,17 @@  public:
   constexpr size_type size () const noexcept { return m_size; }
   constexpr bool empty () const noexcept { return m_size == 0; }
 
+  /* Slice an array view.  */
+
+  /* Return a new array view over SIZE elements starting at START.  */
+  constexpr array_view<T> slice (size_type start, size_type size) const noexcept
+  { return {m_array + start, size}; }
+
+  /* Return a new array view over all the elements after START,
+     inclusive.  */
+  constexpr array_view<T> slice (size_type start) const noexcept
+  { return {m_array + start, size () - start}; }
+
 private:
   T *m_array;
   size_type m_size;
diff --git a/gdb/eval.c b/gdb/eval.c
index 6eb210d109..cb408678a2 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -789,7 +789,9 @@  eval_call (expression *exp, enum noside noside,
       else if (TYPE_CODE (ftype) == TYPE_CODE_XMETHOD)
 	{
 	  type *return_type
-	    = result_type_of_xmethod (argvec[0], nargs, argvec + 1);
+	    = result_type_of_xmethod (argvec[0],
+				      gdb::make_array_view (argvec + 1,
+							    nargs));
 
 	  if (return_type == NULL)
 	    error (_("Xmethod is missing return type."));
@@ -827,7 +829,7 @@  eval_call (expression *exp, enum noside noside,
       return call_internal_function (exp->gdbarch, exp->language_defn,
 				     argvec[0], nargs, argvec + 1);
     case TYPE_CODE_XMETHOD:
-      return call_xmethod (argvec[0], nargs, argvec + 1);
+      return call_xmethod (argvec[0], gdb::make_array_view (argvec + 1, nargs));
     default:
       return call_function_by_hand (argvec[0], default_return_type,
 				    gdb::make_array_view (argvec + 1, nargs));
@@ -1100,7 +1102,8 @@  evaluate_funcall (type *expect_type, expression *exp, int *pos,
       func_name = (char *) alloca (name_len + 1);
       strcpy (func_name, &exp->elts[string_pc + 1].string);
 
-      find_overload_match (&argvec[1], nargs, func_name,
+      find_overload_match (gdb::make_array_view (&argvec[1], nargs),
+			   func_name,
 			   NON_METHOD, /* not method */
 			   NULL, NULL, /* pass NULL symbol since
 					  symbol is unknown */
@@ -1136,7 +1139,8 @@  evaluate_funcall (type *expect_type, expression *exp, int *pos,
 	     evaluation.  */
 	  struct value *valp = NULL;
 
-	  (void) find_overload_match (&argvec[1], nargs, tstr,
+	  (void) find_overload_match (gdb::make_array_view (&argvec[1], nargs),
+				      tstr,
 				      METHOD, /* method */
 				      &arg2,  /* the object */
 				      NULL, &valp, NULL,
@@ -1207,7 +1211,7 @@  evaluate_funcall (type *expect_type, expression *exp, int *pos,
 	  if (op == OP_VAR_VALUE)
 	    function = exp->elts[save_pos1+2].symbol;
 
-	  (void) find_overload_match (&argvec[1], nargs,
+	  (void) find_overload_match (gdb::make_array_view (&argvec[1], nargs),
 				      NULL,        /* no need for name */
 				      NON_METHOD,  /* not method */
 				      NULL, function, /* the function */
diff --git a/gdb/extension.c b/gdb/extension.c
index e5c014667a..31d19b5b09 100644
--- a/gdb/extension.c
+++ b/gdb/extension.c
@@ -870,12 +870,12 @@  get_matching_xmethod_workers (struct type *type, const char *method_name,
 
 /* See extension.h.  */
 
-type **
-xmethod_worker::get_arg_types (int *nargs)
+std::vector<type *>
+xmethod_worker::get_arg_types ()
 {
-  type **type_array = NULL;
+  std::vector<type *> type_array;
 
-  ext_lang_rc rc = do_get_arg_types (nargs, &type_array);
+  ext_lang_rc rc = do_get_arg_types (&type_array);
   if (rc == EXT_LANG_RC_ERROR)
     error (_("Error while looking for arg types of a xmethod worker "
 	     "defined in %s."), m_extlang->capitalized_name);
@@ -886,11 +886,11 @@  xmethod_worker::get_arg_types (int *nargs)
 /* See extension.h.  */
 
 struct type *
-xmethod_worker::get_result_type (value *object, value **args, int nargs)
+xmethod_worker::get_result_type (value *object, gdb::array_view<value *> args)
 {
   type *result_type;
 
-  ext_lang_rc rc = do_get_result_type (object, args, nargs, &result_type);
+  ext_lang_rc rc = do_get_result_type (object, args, &result_type);
   if (rc == EXT_LANG_RC_ERROR)
     {
       error (_("Error while fetching result type of an xmethod worker "
diff --git a/gdb/extension.h b/gdb/extension.h
index 0c8c4ee934..b9314c0efb 100644
--- a/gdb/extension.h
+++ b/gdb/extension.h
@@ -22,6 +22,7 @@ 
 
 #include "mi/mi-cmds.h" /* For PRINT_NO_VALUES, etc.  */
 #include "common/vec.h"
+#include "common/array-view.h"
 
 struct breakpoint;
 struct command_line;
@@ -186,38 +187,35 @@  struct xmethod_worker
   virtual ~xmethod_worker () = default;
 
   /* Invoke the xmethod encapsulated in this worker and return the result.
-     The method is invoked on OBJ with arguments in the ARGS array.  NARGS is
-     the length of the this array.  */
+     The method is invoked on OBJ with arguments in the ARGS array.  */
 
-  virtual value *invoke (value *obj, value **args, int nargs) = 0;
+  virtual value *invoke (value *obj, gdb::array_view<value *> args) = 0;
 
   /* Return the arg types of the xmethod encapsulated in this worker.
-     An array of arg types is returned.  The length of the array is returned in
-     NARGS.  The type of the 'this' object is returned as the first element of
-     array.  */
+     The type of the 'this' object is returned as the first element of
+     the vector.  */
 
-  type **get_arg_types (int *nargs);
+  std::vector<type *> get_arg_types ();
 
   /* Return the type of the result of the xmethod encapsulated in this worker.
-     OBJECT, ARGS, NARGS are the same as for invoke.  */
+     OBJECT and ARGS are the same as for invoke.  */
 
-  type *get_result_type (value *object, value **args, int nargs);
+  type *get_result_type (value *object, gdb::array_view<value *> args);
 
 private:
 
-  /* Return the types of the arguments the method takes.  The number of
-     arguments is returned in NARGS, and their types are returned in the array
-     ARGTYPES.  */
+  /* Return the types of the arguments the method takes.  The types
+     are returned in TYPE_ARGS, one per argument.  */
 
   virtual enum ext_lang_rc do_get_arg_types
-    (int *nargs, struct type ***arg_types) = 0;
+    (std::vector<type *> *type_args) = 0;
 
-  /* Fetch the type of the result of the method implemented by this worker.
-     OBJECT, ARGS, NARGS are the same as for the invoked method.  The result
-     type is stored in *RESULT_TYPE.  */
+  /* Fetch the type of the result of the method implemented by this
+     worker.  OBJECT and ARGS are the same as for the invoked method.
+     The result type is stored in *RESULT_TYPE.  */
 
   virtual enum ext_lang_rc do_get_result_type
-    (struct value *obj, struct value **args, int nargs,
+    (struct value *obj, gdb::array_view<value *> args,
      struct type **result_type_ptr) = 0;
 
   /* The language the xmethod worker is implemented in.  */
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 9e87b8f4c5..4160d996de 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -3464,21 +3464,20 @@  compare_badness (struct badness_vector *a, struct badness_vector *b)
     }
 }
 
-/* Rank a function by comparing its parameter types (PARMS, length
-   NPARMS), to the types of an argument list (ARGS, length NARGS).
-   Return a pointer to a badness vector.  This has NARGS + 1
-   entries.  */
+/* Rank a function by comparing its parameter types (PARMS), to the
+   types of an argument list (ARGS).  Return a pointer to a badness
+   vector.  This has ARGS.size() + 1 entries.  */
 
 struct badness_vector *
-rank_function (struct type **parms, int nparms, 
-	       struct value **args, int nargs)
+rank_function (gdb::array_view<type *> parms,
+	       gdb::array_view<value *> args)
 {
   int i;
   struct badness_vector *bv = XNEW (struct badness_vector);
-  int min_len = nparms < nargs ? nparms : nargs;
+  size_t min_len = std::min (parms.size (), args.size ());
 
-  bv->length = nargs + 1;	/* add 1 for the length-match rank.  */
-  bv->rank = XNEWVEC (struct rank, nargs + 1);
+  bv->length = args.size () + 1;	/* add 1 for the length-match rank.  */
+  bv->rank = XNEWVEC (struct rank, args.size () + 1);
 
   /* First compare the lengths of the supplied lists.
      If there is a mismatch, set it to a high value.  */
@@ -3487,7 +3486,7 @@  rank_function (struct type **parms, int nparms,
      arguments and ellipsis parameter lists, we should consider those
      and rank the length-match more finely.  */
 
-  LENGTH_MATCH (bv) = (nargs != nparms)
+  LENGTH_MATCH (bv) = (args.size () != parms.size ())
 		      ? LENGTH_MISMATCH_BADNESS
 		      : EXACT_MATCH_BADNESS;
 
@@ -3497,7 +3496,7 @@  rank_function (struct type **parms, int nparms,
 				 args[i - 1]);
 
   /* If more arguments than parameters, add dummy entries.  */
-  for (i = min_len + 1; i <= nargs; i++)
+  for (i = min_len + 1; i <= args.size (); i++)
     bv->rank[i] = TOO_FEW_PARAMS_BADNESS;
 
   return bv;
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index a115857c0a..731b18d082 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -45,6 +45,7 @@ 
  */
 
 #include "hashtab.h"
+#include "common/array-view.h"
 #include "common/offset-type.h"
 #include "common/enum-flags.h"
 #include "common/underlying.h"
@@ -2044,8 +2045,8 @@  extern int compare_ranks (struct rank a, struct rank b);
 
 extern int compare_badness (struct badness_vector *, struct badness_vector *);
 
-extern struct badness_vector *rank_function (struct type **, int,
-					     struct value **, int);
+extern struct badness_vector *rank_function (gdb::array_view<type *> parms,
+					     gdb::array_view<value *> args);
 
 extern struct rank rank_one_type (struct type *, struct type *,
 				  struct value *);
diff --git a/gdb/python/py-xmethods.c b/gdb/python/py-xmethods.c
index 8e616cd4e2..1c96b585f7 100644
--- a/gdb/python/py-xmethods.c
+++ b/gdb/python/py-xmethods.c
@@ -46,11 +46,11 @@  struct python_xmethod_worker : xmethod_worker
 
   /* Implementation of xmethod_worker::invoke for Python.  */
 
-  value *invoke (value *obj, value **args, int nargs) override;
+  value *invoke (value *obj, gdb::array_view<value *> args) override;
 
   /* Implementation of xmethod_worker::do_get_arg_types for Python.  */
 
-  ext_lang_rc do_get_arg_types (int *nargs, type ***arg_types) override;
+  ext_lang_rc do_get_arg_types (std::vector<type *> *type_args) override;
 
   /* Implementation of xmethod_worker::do_get_result_type for Python.
 
@@ -58,7 +58,7 @@  struct python_xmethod_worker : xmethod_worker
      result type, if the get_result_type operation is not provided by WORKER
      then EXT_LANG_RC_OK is returned and NULL is returned in *RESULT_TYPE.  */
 
-  ext_lang_rc do_get_result_type (value *obj, value **args, int nargs,
+  ext_lang_rc do_get_result_type (value *obj, gdb::array_view<value *> args,
 				  type **result_type_ptr) override;
 
 private:
@@ -293,7 +293,7 @@  gdbpy_get_matching_xmethod_workers
 /* See declaration.  */
 
 ext_lang_rc
-python_xmethod_worker::do_get_arg_types (int *nargs, type ***arg_types)
+python_xmethod_worker::do_get_arg_types (std::vector<type *> *arg_types)
 {
   /* The gdbpy_enter object needs to be placed first, so that it's the last to
      be destroyed.  */
@@ -302,10 +302,6 @@  python_xmethod_worker::do_get_arg_types (int *nargs, type ***arg_types)
   int i = 1, arg_count;
   gdbpy_ref<> list_iter;
 
-  /* Set nargs to -1 so that any premature return from this function returns
-     an invalid/unusable number of arg types.  */
-  *nargs = -1;
-
   gdbpy_ref<> get_arg_types_method
     (PyObject_GetAttrString (m_py_worker, get_arg_types_method_name));
   if (get_arg_types_method == NULL)
@@ -345,8 +341,7 @@  python_xmethod_worker::do_get_arg_types (int *nargs, type ***arg_types)
     arg_count = 1;
 
   /* Include the 'this' argument in the size.  */
-  gdb::unique_xmalloc_ptr<struct type *> type_array
-    (XCNEWVEC (struct type *, arg_count + 1));
+  arg_types->resize (arg_count + 1);
   i = 1;
   if (list_iter != NULL)
     {
@@ -373,7 +368,7 @@  python_xmethod_worker::do_get_arg_types (int *nargs, type ***arg_types)
 	      return EXT_LANG_RC_ERROR;
 	    }
 
-	  (type_array.get ())[i] = arg_type;
+	  (*arg_types)[i] = arg_type;
 	  i++;
 	}
     }
@@ -393,7 +388,7 @@  python_xmethod_worker::do_get_arg_types (int *nargs, type ***arg_types)
 	}
       else
 	{
-	  (type_array.get ())[i] = arg_type;
+	  (*arg_types)[i] = arg_type;
 	  i++;
 	}
     }
@@ -402,10 +397,8 @@  python_xmethod_worker::do_get_arg_types (int *nargs, type ***arg_types)
      be a 'const' value.  Hence, create a 'const' variant of the 'this' pointer
      type.  */
   obj_type = type_object_to_type (m_this_type);
-  (type_array.get ())[0] = make_cv_type (1, 0, lookup_pointer_type (obj_type),
-					 NULL);
-  *nargs = i;
-  *arg_types = type_array.release ();
+  (*arg_types)[0] = make_cv_type (1, 0, lookup_pointer_type (obj_type),
+				  NULL);
 
   return EXT_LANG_RC_OK;
 }
@@ -413,7 +406,8 @@  python_xmethod_worker::do_get_arg_types (int *nargs, type ***arg_types)
 /* See declaration.  */
 
 ext_lang_rc
-python_xmethod_worker::do_get_result_type (value *obj, value **args, int nargs,
+python_xmethod_worker::do_get_result_type (value *obj,
+					   gdb::array_view<value *> args,
 					   type **result_type_ptr)
 {
   struct type *obj_type, *this_type;
@@ -461,7 +455,7 @@  python_xmethod_worker::do_get_result_type (value *obj, value **args, int nargs,
       return EXT_LANG_RC_ERROR;
     }
 
-  gdbpy_ref<> py_arg_tuple (PyTuple_New (nargs + 1));
+  gdbpy_ref<> py_arg_tuple (PyTuple_New (args.size () + 1));
   if (py_arg_tuple == NULL)
     {
       gdbpy_print_stack ();
@@ -472,7 +466,7 @@  python_xmethod_worker::do_get_result_type (value *obj, value **args, int nargs,
      release.  */
   PyTuple_SET_ITEM (py_arg_tuple.get (), 0, py_value_obj.release ());
 
-  for (i = 0; i < nargs; i++)
+  for (i = 0; i < args.size (); i++)
     {
       PyObject *py_value_arg = value_to_value_object (args[i]);
 
@@ -508,8 +502,8 @@  python_xmethod_worker::do_get_result_type (value *obj, value **args, int nargs,
 /* See declaration.  */
 
 struct value *
-python_xmethod_worker::invoke (struct value *obj, struct value **args,
-			       int nargs)
+python_xmethod_worker::invoke (struct value *obj,
+			       gdb::array_view<value *> args)
 {
   gdbpy_enter enter_py (get_current_arch (), current_language);
 
@@ -546,7 +540,7 @@  python_xmethod_worker::invoke (struct value *obj, struct value **args,
       error (_("Error while executing Python code."));
     }
 
-  gdbpy_ref<> py_arg_tuple (PyTuple_New (nargs + 1));
+  gdbpy_ref<> py_arg_tuple (PyTuple_New (args.size () + 1));
   if (py_arg_tuple == NULL)
     {
       gdbpy_print_stack ();
@@ -557,7 +551,7 @@  python_xmethod_worker::invoke (struct value *obj, struct value **args,
      release.  */
   PyTuple_SET_ITEM (py_arg_tuple.get (), 0, py_value_obj.release ());
 
-  for (i = 0; i < nargs; i++)
+  for (i = 0; i < args.size (); i++)
     {
       PyObject *py_value_arg = value_to_value_object (args[i]);
 
diff --git a/gdb/unittests/array-view-selftests.c b/gdb/unittests/array-view-selftests.c
index 74defa1b40..746062c75a 100644
--- a/gdb/unittests/array-view-selftests.c
+++ b/gdb/unittests/array-view-selftests.c
@@ -496,6 +496,28 @@  run_tests ()
     for (size_t i = 0; i < len; i++)
       SELF_CHECK (view[i] == data[i]);
   }
+
+  /* Test slicing.  */
+  {
+    gdb_byte data[] = {0x55, 0x66, 0x77, 0x88, 0x99};
+    gdb::array_view<gdb_byte> view = data;
+
+    {
+      auto slc = view.slice (1, 3);
+      SELF_CHECK (slc.data () == data + 1);
+      SELF_CHECK (slc.size () == 3);
+      SELF_CHECK (slc[0] == data[1]);
+      SELF_CHECK (slc[0] == view[1]);
+    }
+
+    {
+      auto slc = view.slice (2);
+      SELF_CHECK (slc.data () == data + 2);
+      SELF_CHECK (slc.size () == 3);
+      SELF_CHECK (slc[0] == view[2]);
+      SELF_CHECK (slc[0] == data[2]);
+    }
+  }
 }
 
 } /* namespace array_view_tests */
diff --git a/gdb/valarith.c b/gdb/valarith.c
index 875f5477c2..3a59ada2d5 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -281,14 +281,14 @@  unop_user_defined_p (enum exp_opcode op, struct value *arg1)
    situations or combinations thereof.  */
 
 static struct value *
-value_user_defined_cpp_op (struct value **args, int nargs, char *oper,
+value_user_defined_cpp_op (gdb::array_view<value *> args, char *oper,
                            int *static_memfuncp, enum noside noside)
 {
 
   struct symbol *symp = NULL;
   struct value *valp = NULL;
 
-  find_overload_match (args, nargs, oper, BOTH /* could be method */,
+  find_overload_match (args, oper, BOTH /* could be method */,
                        &args[0] /* objp */,
                        NULL /* pass NULL symbol since symbol is unknown */,
                        &valp, &symp, static_memfuncp, 0, noside);
@@ -312,19 +312,19 @@  value_user_defined_cpp_op (struct value **args, int nargs, char *oper,
    function, otherwise return NULL.  */
 
 static struct value *
-value_user_defined_op (struct value **argp, struct value **args, char *name,
-                       int *static_memfuncp, int nargs, enum noside noside)
+value_user_defined_op (struct value **argp, gdb::array_view<value *> args,
+		       char *name, int *static_memfuncp, enum noside noside)
 {
   struct value *result = NULL;
 
   if (current_language->la_language == language_cplus)
     {
-      result = value_user_defined_cpp_op (args, nargs, name, static_memfuncp,
+      result = value_user_defined_cpp_op (args, name, static_memfuncp,
 					  noside);
     }
   else
-    result = value_struct_elt (argp, args, name, static_memfuncp,
-                               "structure");
+    result = value_struct_elt (argp, args.data (), name, static_memfuncp,
+			       "structure");
 
   return result;
 }
@@ -342,7 +342,6 @@  struct value *
 value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
 	       enum exp_opcode otherop, enum noside noside)
 {
-  struct value **argvec;
   char *ptr;
   char tstr[13];
   int static_memfuncp;
@@ -356,10 +355,11 @@  value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
   if (TYPE_CODE (check_typedef (value_type (arg1))) != TYPE_CODE_STRUCT)
     error (_("Can't do that binary op on that type"));	/* FIXME be explicit */
 
-  argvec = (struct value **) alloca (sizeof (struct value *) * 4);
+  value *argvec_storage[3];
+  gdb::array_view<value *> argvec = argvec_storage;
+
   argvec[1] = value_addr (arg1);
   argvec[2] = arg2;
-  argvec[3] = 0;
 
   /* Make the right function name up.  */
   strcpy (tstr, "operator__");
@@ -469,15 +469,15 @@  value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
       error (_("Invalid binary operation specified."));
     }
 
-  argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr,
-                                     &static_memfuncp, 2, noside);
+  argvec[0] = value_user_defined_op (&arg1, argvec.slice (1), tstr,
+				     &static_memfuncp, noside);
 
   if (argvec[0])
     {
       if (static_memfuncp)
 	{
 	  argvec[1] = argvec[0];
-	  argvec++;
+	  argvec = argvec.slice (1);
 	}
       if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_XMETHOD)
 	{
@@ -486,13 +486,13 @@  value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
 	  if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	    {
 	      struct type *return_type
-		= result_type_of_xmethod (argvec[0], 2, argvec + 1);
+		= result_type_of_xmethod (argvec[0], argvec.slice (1));
 
 	      if (return_type == NULL)
 		error (_("Xmethod is missing return type."));
 	      return value_zero (return_type, VALUE_LVAL (arg1));
 	    }
-	  return call_xmethod (argvec[0], 2, argvec + 1);
+	  return call_xmethod (argvec[0], argvec.slice (1));
 	}
       if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	{
@@ -503,7 +503,7 @@  value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
 	  return value_zero (return_type, VALUE_LVAL (arg1));
 	}
       return call_function_by_hand (argvec[0], NULL,
-				    {argvec + 1, 2u - static_memfuncp});
+				    argvec.slice (1, 2 - static_memfuncp));
     }
   throw_error (NOT_FOUND_ERROR,
                _("member function %s not found"), tstr);
@@ -519,7 +519,6 @@  struct value *
 value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
 {
   struct gdbarch *gdbarch = get_type_arch (value_type (arg1));
-  struct value **argvec;
   char *ptr;
   char tstr[13], mangle_tstr[13];
   int static_memfuncp, nargs;
@@ -532,7 +531,9 @@  value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
   if (TYPE_CODE (check_typedef (value_type (arg1))) != TYPE_CODE_STRUCT)
     error (_("Can't do that unary op on that type"));	/* FIXME be explicit */
 
-  argvec = (struct value **) alloca (sizeof (struct value *) * 4);
+  value *argvec_storage[3];
+  gdb::array_view<value *> argvec = argvec_storage;
+
   argvec[1] = value_addr (arg1);
   argvec[2] = 0;
 
@@ -584,16 +585,15 @@  value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
       error (_("Invalid unary operation specified."));
     }
 
-  argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr,
-                                     &static_memfuncp, nargs, noside);
+  argvec[0] = value_user_defined_op (&arg1, argvec.slice (1, nargs), tstr,
+				     &static_memfuncp, noside);
 
   if (argvec[0])
     {
       if (static_memfuncp)
 	{
 	  argvec[1] = argvec[0];
-	  nargs --;
-	  argvec++;
+	  argvec = argvec.slice (1);
 	}
       if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_XMETHOD)
 	{
@@ -602,13 +602,13 @@  value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
 	  if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	    {
 	      struct type *return_type
-		= result_type_of_xmethod (argvec[0], 1, argvec + 1);
+		= result_type_of_xmethod (argvec[0], argvec[1]);
 
 	      if (return_type == NULL)
 		error (_("Xmethod is missing return type."));
 	      return value_zero (return_type, VALUE_LVAL (arg1));
 	    }
-	  return call_xmethod (argvec[0], 1, argvec + 1);
+	  return call_xmethod (argvec[0], argvec[1]);
 	}
       if (noside == EVAL_AVOID_SIDE_EFFECTS)
 	{
@@ -619,7 +619,7 @@  value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
 	  return value_zero (return_type, VALUE_LVAL (arg1));
 	}
       return call_function_by_hand (argvec[0], NULL,
-				    gdb::make_array_view (argvec + 1, nargs));
+				    argvec.slice (1, nargs));
     }
   throw_error (NOT_FOUND_ERROR,
                _("member function %s not found"), tstr);
diff --git a/gdb/valops.c b/gdb/valops.c
index 4758b5cdfc..f0e53a7ce9 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -54,20 +54,19 @@  static struct value *search_struct_method (const char *, struct value **,
 					   struct value **,
 					   LONGEST, int *, struct type *);
 
-static int find_oload_champ_namespace (struct value **, int,
+static int find_oload_champ_namespace (gdb::array_view<value *> args,
 				       const char *, const char *,
 				       struct symbol ***,
 				       struct badness_vector **,
 				       const int no_adl);
 
-static
-int find_oload_champ_namespace_loop (struct value **, int,
-				     const char *, const char *,
-				     int, struct symbol ***,
-				     struct badness_vector **, int *,
-				     const int no_adl);
+static int find_oload_champ_namespace_loop (gdb::array_view<value *> args,
+					    const char *, const char *,
+					    int, struct symbol ***,
+					    struct badness_vector **, int *,
+					    const int no_adl);
 
-static int find_oload_champ (struct value **, int, int,
+static int find_oload_champ (gdb::array_view<value *> args, int,
 			     struct fn_field *,
 			     const std::vector<xmethod_worker_up> *,
 			     struct symbol **, struct badness_vector **);
@@ -2446,11 +2445,11 @@  value_find_oload_method_list (struct value **argp, const char *method,
 		    basetype, boffset);
 }
 
-/* Given an array of arguments (ARGS) (which includes an
-   entry for "this" in the case of C++ methods), the number of
-   arguments NARGS, the NAME of a function, and whether it's a method or
-   not (METHOD), find the best function that matches on the argument types
-   according to the overload resolution rules.
+/* Given an array of arguments (ARGS) (which includes an entry for
+   "this" in the case of C++ methods), the NAME of a function, and
+   whether it's a method or not (METHOD), find the best function that
+   matches on the argument types according to the overload resolution
+   rules.
 
    METHOD can be one of three values:
      NON_METHOD for non-member functions.
@@ -2493,7 +2492,7 @@  value_find_oload_method_list (struct value **argp, const char *method,
    resolution is permitted.  */
 
 int
-find_overload_match (struct value **args, int nargs,
+find_overload_match (gdb::array_view<value *> args,
 		     const char *name, enum oload_search_type method,
 		     struct value **objp, struct symbol *fsym,
 		     struct value **valp, struct symbol **symp, 
@@ -2578,12 +2577,12 @@  find_overload_match (struct value **args, int nargs,
 	{
 	  gdb_assert (TYPE_SELF_TYPE (fns_ptr[0].type) != NULL);
 
-	  src_method_oload_champ = find_oload_champ (args, nargs,
+	  src_method_oload_champ = find_oload_champ (args,
 						     num_fns, fns_ptr, NULL,
 						     NULL, &src_method_badness);
 
 	  src_method_match_quality = classify_oload_match
-	    (src_method_badness, nargs,
+	    (src_method_badness, args.size (),
 	     oload_method_static_p (fns_ptr, src_method_oload_champ));
 
 	  make_cleanup (xfree, src_method_badness);
@@ -2591,11 +2590,10 @@  find_overload_match (struct value **args, int nargs,
 
       if (!xm_worker_vec.empty ())
 	{
-	  ext_method_oload_champ = find_oload_champ (args, nargs,
-						     0, NULL, &xm_worker_vec,
+	  ext_method_oload_champ = find_oload_champ (args, 0, NULL, &xm_worker_vec,
 						     NULL, &ext_method_badness);
 	  ext_method_match_quality = classify_oload_match (ext_method_badness,
-							   nargs, 0);
+							   args.size (), 0);
 	  make_cleanup (xfree, ext_method_badness);
 	}
 
@@ -2708,7 +2706,7 @@  find_overload_match (struct value **args, int nargs,
           return 0;
         }
 
-      func_oload_champ = find_oload_champ_namespace (args, nargs,
+      func_oload_champ = find_oload_champ_namespace (args,
                                                      func_name,
                                                      qualified_name,
                                                      &oload_syms,
@@ -2716,7 +2714,8 @@  find_overload_match (struct value **args, int nargs,
                                                      no_adl);
 
       if (func_oload_champ >= 0)
-	func_match_quality = classify_oload_match (func_badness, nargs, 0);
+	func_match_quality = classify_oload_match (func_badness,
+						   args.size (), 0);
 
       make_cleanup (xfree, oload_syms);
       make_cleanup (xfree, func_badness);
@@ -2855,7 +2854,7 @@  find_overload_match (struct value **args, int nargs,
    performned.  */
 
 static int
-find_oload_champ_namespace (struct value **args, int nargs,
+find_oload_champ_namespace (gdb::array_view<value *> args,
 			    const char *func_name,
 			    const char *qualified_name,
 			    struct symbol ***oload_syms,
@@ -2864,7 +2863,7 @@  find_oload_champ_namespace (struct value **args, int nargs,
 {
   int oload_champ;
 
-  find_oload_champ_namespace_loop (args, nargs,
+  find_oload_champ_namespace_loop (args,
 				   func_name,
 				   qualified_name, 0,
 				   oload_syms, oload_champ_bv,
@@ -2884,7 +2883,7 @@  find_oload_champ_namespace (struct value **args, int nargs,
    *OLOAD_CHAMP_BV.  */
 
 static int
-find_oload_champ_namespace_loop (struct value **args, int nargs,
+find_oload_champ_namespace_loop (gdb::array_view<value *> args,
 				 const char *func_name,
 				 const char *qualified_name,
 				 int namespace_len,
@@ -2921,7 +2920,7 @@  find_oload_champ_namespace_loop (struct value **args, int nargs,
     {
       searched_deeper = 1;
 
-      if (find_oload_champ_namespace_loop (args, nargs,
+      if (find_oload_champ_namespace_loop (args,
 					   func_name, qualified_name,
 					   next_namespace_len,
 					   oload_syms, oload_champ_bv,
@@ -2956,16 +2955,16 @@  find_oload_champ_namespace_loop (struct value **args, int nargs,
 
       /* Prepare list of argument types for overload resolution.  */
       arg_types = (struct type **)
-	alloca (nargs * (sizeof (struct type *)));
-      for (ix = 0; ix < nargs; ix++)
+	alloca (args.size () * (sizeof (struct type *)));
+      for (ix = 0; ix < args.size (); ix++)
 	arg_types[ix] = value_type (args[ix]);
-      make_symbol_overload_list_adl (arg_types, nargs, func_name);
+      make_symbol_overload_list_adl (arg_types, args.size (), func_name);
     }
 
   while (new_oload_syms[num_fns])
     ++num_fns;
 
-  new_oload_champ = find_oload_champ (args, nargs, num_fns,
+  new_oload_champ = find_oload_champ (args, num_fns,
 				      NULL, NULL, new_oload_syms,
 				      &new_oload_champ_bv);
 
@@ -2977,7 +2976,7 @@  find_oload_champ_namespace_loop (struct value **args, int nargs,
      it's a bad match.  */
 
   if (new_oload_champ != -1
-      && classify_oload_match (new_oload_champ_bv, nargs, 0) == STANDARD)
+      && classify_oload_match (new_oload_champ_bv, args.size (), 0) == STANDARD)
     {
       *oload_syms = new_oload_syms;
       *oload_champ = new_oload_champ;
@@ -3002,11 +3001,10 @@  find_oload_champ_namespace_loop (struct value **args, int nargs,
     }
 }
 
-/* Look for a function to take NARGS args of ARGS.  Find
-   the best match from among the overloaded methods or functions
-   given by FNS_PTR or OLOAD_SYMS or XM_WORKER_VEC, respectively.
-   One, and only one of FNS_PTR, OLOAD_SYMS and XM_WORKER_VEC can be
-   non-NULL.
+/* Look for a function to take ARGS.  Find the best match from among
+   the overloaded methods or functions given by FNS_PTR or OLOAD_SYMS
+   or XM_WORKER_VEC, respectively.  One, and only one of FNS_PTR,
+   OLOAD_SYMS and XM_WORKER_VEC can be non-NULL.
 
    If XM_WORKER_VEC is NULL, then the length of the arrays FNS_PTR
    or OLOAD_SYMS (whichever is non-NULL) is specified in NUM_FNS.
@@ -3017,7 +3015,7 @@  find_oload_champ_namespace_loop (struct value **args, int nargs,
    It is the caller's responsibility to free *OLOAD_CHAMP_BV.  */
 
 static int
-find_oload_champ (struct value **args, int nargs,
+find_oload_champ (gdb::array_view<value *> args,
 		  int num_fns, struct fn_field *fns_ptr,
 		  const std::vector<xmethod_worker_up> *xm_worker_vec,
 		  struct symbol **oload_syms,
@@ -3047,16 +3045,17 @@  find_oload_champ (struct value **args, int nargs,
     {
       int jj;
       int static_offset = 0;
-      int nparms;
-      struct type **parm_types;
+      std::vector<type *> parm_types;
 
       if (xm_worker_vec != NULL)
 	{
 	  xmethod_worker *worker = (*xm_worker_vec)[ix].get ();
-	  parm_types = worker->get_arg_types (&nparms);
+	  parm_types = worker->get_arg_types ();
 	}
       else
 	{
+	  size_t nparms;
+
 	  if (fns_ptr != NULL)
 	    {
 	      nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix));
@@ -3065,19 +3064,21 @@  find_oload_champ (struct value **args, int nargs,
 	  else
 	    nparms = TYPE_NFIELDS (SYMBOL_TYPE (oload_syms[ix]));
 
-	  parm_types = XNEWVEC (struct type *, nparms);
+	  parm_types.reserve (nparms);
 	  for (jj = 0; jj < nparms; jj++)
-	    parm_types[jj] = (fns_ptr != NULL
-			      ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type)
-			      : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]),
-						 jj));
+	    {
+	      type *t = (fns_ptr != NULL
+			 ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type)
+			 : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]),
+					    jj));
+	      parm_types.push_back (t);
+	    }
 	}
 
       /* Compare parameter types to supplied argument types.  Skip
          THIS for static methods.  */
-      bv = rank_function (parm_types, nparms, 
-			  args + static_offset,
-			  nargs - static_offset);
+      bv = rank_function (parm_types,
+			  args.slice (static_offset));
 
       if (!*oload_champ_bv)
 	{
@@ -3103,24 +3104,23 @@  find_oload_champ (struct value **args, int nargs,
 	  default:
 	    break;
 	  }
-      xfree (parm_types);
       if (overload_debug)
 	{
 	  if (fns_ptr != NULL)
 	    fprintf_filtered (gdb_stderr,
 			      "Overloaded method instance %s, # of parms %d\n",
-			      fns_ptr[ix].physname, nparms);
+			      fns_ptr[ix].physname, (int) parm_types.size ());
 	  else if (xm_worker_vec != NULL)
 	    fprintf_filtered (gdb_stderr,
 			      "Xmethod worker, # of parms %d\n",
-			      nparms);
+			      (int) parm_types.size ());
 	  else
 	    fprintf_filtered (gdb_stderr,
 			      "Overloaded function instance "
 			      "%s # of parms %d\n",
 			      SYMBOL_DEMANGLED_NAME (oload_syms[ix]), 
-			      nparms);
-	  for (jj = 0; jj < nargs - static_offset; jj++)
+			      (int) parm_types.size ());
+	  for (jj = 0; jj < args.size () - static_offset; jj++)
 	    fprintf_filtered (gdb_stderr,
 			      "...Badness @ %d : %d\n", 
 			      jj, bv->rank[jj].rank);
diff --git a/gdb/value.c b/gdb/value.c
index 38fc18e0f6..c7b8468b39 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -2583,24 +2583,23 @@  value_from_xmethod (xmethod_worker_up &&worker)
 /* Return the type of the result of TYPE_CODE_XMETHOD value METHOD.  */
 
 struct type *
-result_type_of_xmethod (struct value *method, int argc, struct value **argv)
+result_type_of_xmethod (struct value *method, gdb::array_view<value *> argv)
 {
   gdb_assert (TYPE_CODE (value_type (method)) == TYPE_CODE_XMETHOD
-	      && method->lval == lval_xcallable && argc > 0);
+	      && method->lval == lval_xcallable && !argv.empty ());
 
-  return method->location.xm_worker->get_result_type
-    (argv[0], argv + 1, argc - 1);
+  return method->location.xm_worker->get_result_type (argv[0], argv.slice (1));
 }
 
 /* Call the xmethod corresponding to the TYPE_CODE_XMETHOD value METHOD.  */
 
 struct value *
-call_xmethod (struct value *method, int argc, struct value **argv)
+call_xmethod (struct value *method, gdb::array_view<value *> argv)
 {
   gdb_assert (TYPE_CODE (value_type (method)) == TYPE_CODE_XMETHOD
-	      && method->lval == lval_xcallable && argc > 0);
+	      && method->lval == lval_xcallable && !argv.empty ());
 
-  return method->location.xm_worker->invoke (argv[0], argv + 1, argc - 1);
+  return method->location.xm_worker->invoke (argv[0], argv.slice (1));
 }
 
 /* Extract a value as a C number (either long or double).
diff --git a/gdb/value.h b/gdb/value.h
index 4d75c966ed..54caa58e7f 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -835,7 +835,7 @@  extern struct value *value_static_field (struct type *type, int fieldno);
 
 enum oload_search_type { NON_METHOD, METHOD, BOTH };
 
-extern int find_overload_match (struct value **args, int nargs,
+extern int find_overload_match (gdb::array_view<value *> args,
 				const char *name,
 				enum oload_search_type method,
 				struct value **objp, struct symbol *fsym,
@@ -1175,10 +1175,10 @@  char *value_internal_function_name (struct value *);
 extern struct value *value_from_xmethod (xmethod_worker_up &&worker);
 
 extern struct type *result_type_of_xmethod (struct value *method,
-					    int argc, struct value **argv);
+					    gdb::array_view<value *> argv);
 
 extern struct value *call_xmethod (struct value *method,
-				   int argc, struct value **argv);
+				   gdb::array_view<value *> argv);
 
 /* Given a discriminated union type and some corresponding value
    contents, this will return the field index of the currently active