diff mbox series

[1/4] c++: Fix ICE with variadic concepts and aliases [PR93907]

Message ID 20201106014032.3841458-1-ppalka@redhat.com
State New
Headers show
Series [1/4] c++: Fix ICE with variadic concepts and aliases [PR93907] | expand

Commit Message

Patrick Palka Nov. 6, 2020, 1:40 a.m. UTC
This patch (naively) extends the PR93907 fix to also apply to variadic
concepts invoked with a type argument pack.  Without this, we ICE on
the below testcase (a variadic version of concepts-using2.C) in the same
manner as we used to on concepts-using2.C before r10-7133.

Patch series bootstrapped and regtested on x86_64-pc-linux-gnu,
and also tested against cmcstl2 and range-v3.

gcc/cp/ChangeLog:

	PR c++/93907
	* constraint.cc (tsubst_parameter_mapping): Also canonicalize
	the type arguments of a TYPE_ARGUMENT_PACk.

gcc/testsuite/ChangeLog:

	PR c++/93907
	* g++.dg/cpp2a/concepts-using3.C: New test, based off of
	concepts-using2.C.
---
 gcc/cp/constraint.cc                         | 10 ++++
 gcc/testsuite/g++.dg/cpp2a/concepts-using3.C | 52 ++++++++++++++++++++
 2 files changed, 62 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-using3.C

Comments

Jason Merrill Nov. 6, 2020, 8 p.m. UTC | #1
On 11/5/20 8:40 PM, Patrick Palka wrote:
> This patch (naively) extends the PR93907 fix to also apply to variadic
> concepts invoked with a type argument pack.  Without this, we ICE on
> the below testcase (a variadic version of concepts-using2.C) in the same
> manner as we used to on concepts-using2.C before r10-7133.
> 
> Patch series bootstrapped and regtested on x86_64-pc-linux-gnu,
> and also tested against cmcstl2 and range-v3.
> 
> gcc/cp/ChangeLog:
> 
> 	PR c++/93907
> 	* constraint.cc (tsubst_parameter_mapping): Also canonicalize
> 	the type arguments of a TYPE_ARGUMENT_PACk.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR c++/93907
> 	* g++.dg/cpp2a/concepts-using3.C: New test, based off of
> 	concepts-using2.C.
> ---
>   gcc/cp/constraint.cc                         | 10 ++++
>   gcc/testsuite/g++.dg/cpp2a/concepts-using3.C | 52 ++++++++++++++++++++
>   2 files changed, 62 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index b6f6f0d02a5..c871a8ab86a 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -2252,6 +2252,16 @@ tsubst_parameter_mapping (tree map, tree args, subst_info info)

Hmm, the

 >       else if (ARGUMENT_PACK_P (arg))
 >         new_arg = tsubst_argument_pack (arg, args, complain, in_decl);

just above this seems redundant, since tsubst_template_arg handles packs 
just fine.  In fact, I wonder why tsubst_argument_pack is used 
specifically anywhere?  It seems to get some edge cases better than the 
code in tsubst, but the solution to that would seem to be replacing the 
code in tsubst with a call to tsubst_argument_pack; then we can remove 
all the other calls to the function.

>   	  new_arg = tsubst_template_arg (arg, args, complain, in_decl);
>   	  if (TYPE_P (new_arg))
>   	    new_arg = canonicalize_type_argument (new_arg, complain);
> +	  if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK)
> +	    {
> +	      tree pack_args = ARGUMENT_PACK_ARGS (new_arg);
> +	      for (int i = 0; i < TREE_VEC_LENGTH (pack_args); i++)
> +		{
> +		  tree& pack_arg = TREE_VEC_ELT (pack_args, i);
> +		  if (TYPE_P (pack_arg))
> +		    pack_arg = canonicalize_type_argument (pack_arg, complain);

Do we need the TYPE_P here, since we already know we're in a 
TYPE_ARGUMENT_PACK?  That is, can an element of a TYPE_ARGUMENT_PACK be 
an invalid argument to canonicalize_type_argument?

OTOH, I wonder if we need to canonicalize non-type arguments here as well?

I wonder if tsubst_template_arg should canonicalize rather than leave 
that up to the caller?  I suppose that could do a bit more work when the 
result is going to end up in convert_template_argument and get 
canonicalized again; I don't know if that would be significant.

>   	}
>         if (new_arg == error_mark_node)
>   	return error_mark_node;
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
> new file mode 100644
> index 00000000000..2c8ad40d104
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
> @@ -0,0 +1,52 @@
> +// PR c++/93907
> +// { dg-options -std=gnu++20 }
> +
> +// This testcase is a variadic version of concepts-using2.C; the only
> +// difference is that 'cd' and 'ce' are now variadic concepts.
> +
> +template <int a> struct c {
> +  static constexpr int d = a;
> +  typedef c e;
> +};
> +template <typename> struct f;
> +template <typename b> using g = typename f<b>::e;
> +struct b;
> +template <typename b> struct f { using e = b; };
> +template <typename ai> struct m { typedef g<ai> aj; };
> +template <typename b> struct n { typedef typename m<b>::aj e; };
> +template <typename b> using an = typename n<b>::e;
> +template <typename> constexpr bool ao = c<true>::d;
> +template <typename> constexpr bool i = c<1>::d;
> +template <typename> concept bb = i<b>;
> +#ifdef __SIZEOF_INT128__
> +using cc = __int128;
> +#else
> +using cc = long long;
> +#endif
> +template <typename...> concept cd = bb<cc>;
> +template <typename... bt> concept ce = requires { requires cd<bt...>; };
> +template <typename bt> concept h = ce<bt>;
> +template <typename bt> concept l = h<bt>;
> +template <typename> concept cl = ao<b>;
> +template <typename b> concept cp = requires(b j) {
> +  requires h<an<decltype(j.begin())>>;
> +};
> +struct o {
> +  template <cl b> requires cp<b> auto operator()(b) {}
> +};
> +template <typename b> using cm = decltype(o{}(b()));
> +template <typename bt> concept ct = l<bt>;
> +template <typename da> concept dd = ct<cm<da>>;
> +template <typename da> concept de = dd<da>;
> +struct {
> +  template <de da, typename b> void operator()(da, b);
> +} di;
> +struct p {
> +  void begin();
> +};
> +template <typename> using df = p;
> +template <int> void q() {
> +  df<int> k;
> +  int d;
> +  di(k, d);
> +}
>
Patrick Palka Nov. 7, 2020, 3:59 p.m. UTC | #2
On Fri, 6 Nov 2020, Jason Merrill wrote:

> On 11/5/20 8:40 PM, Patrick Palka wrote:
> > This patch (naively) extends the PR93907 fix to also apply to variadic
> > concepts invoked with a type argument pack.  Without this, we ICE on
> > the below testcase (a variadic version of concepts-using2.C) in the same
> > manner as we used to on concepts-using2.C before r10-7133.
> > 
> > Patch series bootstrapped and regtested on x86_64-pc-linux-gnu,
> > and also tested against cmcstl2 and range-v3.
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	PR c++/93907
> > 	* constraint.cc (tsubst_parameter_mapping): Also canonicalize
> > 	the type arguments of a TYPE_ARGUMENT_PACk.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	PR c++/93907
> > 	* g++.dg/cpp2a/concepts-using3.C: New test, based off of
> > 	concepts-using2.C.
> > ---
> >   gcc/cp/constraint.cc                         | 10 ++++
> >   gcc/testsuite/g++.dg/cpp2a/concepts-using3.C | 52 ++++++++++++++++++++
> >   2 files changed, 62 insertions(+)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
> > 
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index b6f6f0d02a5..c871a8ab86a 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -2252,6 +2252,16 @@ tsubst_parameter_mapping (tree map, tree args,
> > subst_info info)
> 
> Hmm, the
> 
> >       else if (ARGUMENT_PACK_P (arg))
> >         new_arg = tsubst_argument_pack (arg, args, complain, in_decl);
> 
> just above this seems redundant, since tsubst_template_arg handles packs just
> fine.  In fact, I wonder why tsubst_argument_pack is used specifically
> anywhere?  It seems to get some edge cases better than the code in tsubst, but
> the solution to that would seem to be replacing the code in tsubst with a call
> to tsubst_argument_pack; then we can remove all the other calls to the
> function.

They seem interchangeable here wrt handling TYPE_ARGUMENT_PACKs, but not
NONTYPE_ARGUMENT_PACKs.  It looks like tsubst_template_arg ends up just
issuing an error from tsubst_expr if we try using it to substitute into
a NONTYPE_ARGUMENT_PACK.

> 
> >   	  new_arg = tsubst_template_arg (arg, args, complain, in_decl);
> >   	  if (TYPE_P (new_arg))
> >   	    new_arg = canonicalize_type_argument (new_arg, complain);
> > +	  if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK)
> > +	    {
> > +	      tree pack_args = ARGUMENT_PACK_ARGS (new_arg);
> > +	      for (int i = 0; i < TREE_VEC_LENGTH (pack_args); i++)
> > +		{
> > +		  tree& pack_arg = TREE_VEC_ELT (pack_args, i);
> > +		  if (TYPE_P (pack_arg))
> > +		    pack_arg = canonicalize_type_argument (pack_arg,
> > complain);
> 
> Do we need the TYPE_P here, since we already know we're in a
> TYPE_ARGUMENT_PACK?  That is, can an element of a TYPE_ARGUMENT_PACK be an
> invalid argument to canonicalize_type_argument?

With -fconcepts-ts, the elements of a TYPE_ARGUMENT_PACK here can be
TEMPLATE_DECLs, as in e.g. line 28 of concepts/template-parm3.C.

> 
> OTOH, I wonder if we need to canonicalize non-type arguments here as well?

Hmm, I'm not sure.  Not doing so should at worst result in a
satisfaction cache miss in release builds, and in checking builds should
get caught by the hash table sanitizer.  I haven't been able to come up
with a testcase that demonstrates it's necessary.

> 
> I wonder if tsubst_template_arg should canonicalize rather than leave that up
> to the caller?  I suppose that could do a bit more work when the result is
> going to end up in convert_template_argument and get canonicalized again; I
> don't know if that would be significant.

That seems like it works, based on some limited testing.  But there are
only two users of canonicalize_template_argument outside of
convert_template_argument itself, and the one use in unify is still
needed even with this change (or else we get many ICEs coming from
verify_unstripped_args if we try to remove it).  So the benefit of such
a change seems marginal at the moment.

> 
> >   	}
> >         if (new_arg == error_mark_node)
> >   	return error_mark_node;
> > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
> > b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
> > new file mode 100644
> > index 00000000000..2c8ad40d104
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
> > @@ -0,0 +1,52 @@
> > +// PR c++/93907
> > +// { dg-options -std=gnu++20 }
> > +
> > +// This testcase is a variadic version of concepts-using2.C; the only
> > +// difference is that 'cd' and 'ce' are now variadic concepts.
> > +
> > +template <int a> struct c {
> > +  static constexpr int d = a;
> > +  typedef c e;
> > +};
> > +template <typename> struct f;
> > +template <typename b> using g = typename f<b>::e;
> > +struct b;
> > +template <typename b> struct f { using e = b; };
> > +template <typename ai> struct m { typedef g<ai> aj; };
> > +template <typename b> struct n { typedef typename m<b>::aj e; };
> > +template <typename b> using an = typename n<b>::e;
> > +template <typename> constexpr bool ao = c<true>::d;
> > +template <typename> constexpr bool i = c<1>::d;
> > +template <typename> concept bb = i<b>;
> > +#ifdef __SIZEOF_INT128__
> > +using cc = __int128;
> > +#else
> > +using cc = long long;
> > +#endif
> > +template <typename...> concept cd = bb<cc>;
> > +template <typename... bt> concept ce = requires { requires cd<bt...>; };
> > +template <typename bt> concept h = ce<bt>;
> > +template <typename bt> concept l = h<bt>;
> > +template <typename> concept cl = ao<b>;
> > +template <typename b> concept cp = requires(b j) {
> > +  requires h<an<decltype(j.begin())>>;
> > +};
> > +struct o {
> > +  template <cl b> requires cp<b> auto operator()(b) {}
> > +};
> > +template <typename b> using cm = decltype(o{}(b()));
> > +template <typename bt> concept ct = l<bt>;
> > +template <typename da> concept dd = ct<cm<da>>;
> > +template <typename da> concept de = dd<da>;
> > +struct {
> > +  template <de da, typename b> void operator()(da, b);
> > +} di;
> > +struct p {
> > +  void begin();
> > +};
> > +template <typename> using df = p;
> > +template <int> void q() {
> > +  df<int> k;
> > +  int d;
> > +  di(k, d);
> > +}
> > 
> 
>
Jason Merrill Nov. 9, 2020, 7:55 p.m. UTC | #3
On 11/7/20 10:59 AM, Patrick Palka wrote:
> On Fri, 6 Nov 2020, Jason Merrill wrote:
> 
>> On 11/5/20 8:40 PM, Patrick Palka wrote:
>>> This patch (naively) extends the PR93907 fix to also apply to variadic
>>> concepts invoked with a type argument pack.  Without this, we ICE on
>>> the below testcase (a variadic version of concepts-using2.C) in the same
>>> manner as we used to on concepts-using2.C before r10-7133.
>>>
>>> Patch series bootstrapped and regtested on x86_64-pc-linux-gnu,
>>> and also tested against cmcstl2 and range-v3.
>>>
>>> gcc/cp/ChangeLog:
>>>
>>> 	PR c++/93907
>>> 	* constraint.cc (tsubst_parameter_mapping): Also canonicalize
>>> 	the type arguments of a TYPE_ARGUMENT_PACk.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> 	PR c++/93907
>>> 	* g++.dg/cpp2a/concepts-using3.C: New test, based off of
>>> 	concepts-using2.C.
>>> ---
>>>    gcc/cp/constraint.cc                         | 10 ++++
>>>    gcc/testsuite/g++.dg/cpp2a/concepts-using3.C | 52 ++++++++++++++++++++
>>>    2 files changed, 62 insertions(+)
>>>    create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
>>>
>>> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
>>> index b6f6f0d02a5..c871a8ab86a 100644
>>> --- a/gcc/cp/constraint.cc
>>> +++ b/gcc/cp/constraint.cc
>>> @@ -2252,6 +2252,16 @@ tsubst_parameter_mapping (tree map, tree args,
>>> subst_info info)
>>
>> Hmm, the
>>
>>>        else if (ARGUMENT_PACK_P (arg))
>>>          new_arg = tsubst_argument_pack (arg, args, complain, in_decl);
>>
>> just above this seems redundant, since tsubst_template_arg handles packs just
>> fine.  In fact, I wonder why tsubst_argument_pack is used specifically
>> anywhere?  It seems to get some edge cases better than the code in tsubst, but
>> the solution to that would seem to be replacing the code in tsubst with a call
>> to tsubst_argument_pack; then we can remove all the other calls to the
>> function.
> 
> They seem interchangeable here wrt handling TYPE_ARGUMENT_PACKs, but not
> NONTYPE_ARGUMENT_PACKs.  It looks like tsubst_template_arg ends up just
> issuing an error from tsubst_expr if we try using it to substitute into
> a NONTYPE_ARGUMENT_PACK.
> 
>>
>>>    	  new_arg = tsubst_template_arg (arg, args, complain, in_decl);
>>>    	  if (TYPE_P (new_arg))
>>>    	    new_arg = canonicalize_type_argument (new_arg, complain);
>>> +	  if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK)
>>> +	    {
>>> +	      tree pack_args = ARGUMENT_PACK_ARGS (new_arg);
>>> +	      for (int i = 0; i < TREE_VEC_LENGTH (pack_args); i++)
>>> +		{
>>> +		  tree& pack_arg = TREE_VEC_ELT (pack_args, i);
>>> +		  if (TYPE_P (pack_arg))
>>> +		    pack_arg = canonicalize_type_argument (pack_arg,
>>> complain);
>>
>> Do we need the TYPE_P here, since we already know we're in a
>> TYPE_ARGUMENT_PACK?  That is, can an element of a TYPE_ARGUMENT_PACK be an
>> invalid argument to canonicalize_type_argument?
> 
> With -fconcepts-ts, the elements of a TYPE_ARGUMENT_PACK here can be
> TEMPLATE_DECLs, as in e.g. line 28 of concepts/template-parm3.C.
> 
>>
>> OTOH, I wonder if we need to canonicalize non-type arguments here as well?
> 
> Hmm, I'm not sure.  Not doing so should at worst result in a
> satisfaction cache miss in release builds, and in checking builds should
> get caught by the hash table sanitizer.  I haven't been able to come up
> with a testcase that demonstrates it's necessary.
> 
>>
>> I wonder if tsubst_template_arg should canonicalize rather than leave that up
>> to the caller?  I suppose that could do a bit more work when the result is
>> going to end up in convert_template_argument and get canonicalized again; I
>> don't know if that would be significant.
> 
> That seems like it works, based on some limited testing.  But there are
> only two users of canonicalize_template_argument outside of
> convert_template_argument itself, and the one use in unify is still
> needed even with this change (or else we get many ICEs coming from
> verify_unstripped_args if we try to remove it).  So the benefit of such
> a change seems marginal at the moment.

Then the patch is OK as is.

>>>    	}
>>>          if (new_arg == error_mark_node)
>>>    	return error_mark_node;
>>> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
>>> b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
>>> new file mode 100644
>>> index 00000000000..2c8ad40d104
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
>>> @@ -0,0 +1,52 @@
>>> +// PR c++/93907
>>> +// { dg-options -std=gnu++20 }
>>> +
>>> +// This testcase is a variadic version of concepts-using2.C; the only
>>> +// difference is that 'cd' and 'ce' are now variadic concepts.
>>> +
>>> +template <int a> struct c {
>>> +  static constexpr int d = a;
>>> +  typedef c e;
>>> +};
>>> +template <typename> struct f;
>>> +template <typename b> using g = typename f<b>::e;
>>> +struct b;
>>> +template <typename b> struct f { using e = b; };
>>> +template <typename ai> struct m { typedef g<ai> aj; };
>>> +template <typename b> struct n { typedef typename m<b>::aj e; };
>>> +template <typename b> using an = typename n<b>::e;
>>> +template <typename> constexpr bool ao = c<true>::d;
>>> +template <typename> constexpr bool i = c<1>::d;
>>> +template <typename> concept bb = i<b>;
>>> +#ifdef __SIZEOF_INT128__
>>> +using cc = __int128;
>>> +#else
>>> +using cc = long long;
>>> +#endif
>>> +template <typename...> concept cd = bb<cc>;
>>> +template <typename... bt> concept ce = requires { requires cd<bt...>; };
>>> +template <typename bt> concept h = ce<bt>;
>>> +template <typename bt> concept l = h<bt>;
>>> +template <typename> concept cl = ao<b>;
>>> +template <typename b> concept cp = requires(b j) {
>>> +  requires h<an<decltype(j.begin())>>;
>>> +};
>>> +struct o {
>>> +  template <cl b> requires cp<b> auto operator()(b) {}
>>> +};
>>> +template <typename b> using cm = decltype(o{}(b()));
>>> +template <typename bt> concept ct = l<bt>;
>>> +template <typename da> concept dd = ct<cm<da>>;
>>> +template <typename da> concept de = dd<da>;
>>> +struct {
>>> +  template <de da, typename b> void operator()(da, b);
>>> +} di;
>>> +struct p {
>>> +  void begin();
>>> +};
>>> +template <typename> using df = p;
>>> +template <int> void q() {
>>> +  df<int> k;
>>> +  int d;
>>> +  di(k, d);
>>> +}
>>>
>>
>>
>
diff mbox series

Patch

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index b6f6f0d02a5..c871a8ab86a 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -2252,6 +2252,16 @@  tsubst_parameter_mapping (tree map, tree args, subst_info info)
 	  new_arg = tsubst_template_arg (arg, args, complain, in_decl);
 	  if (TYPE_P (new_arg))
 	    new_arg = canonicalize_type_argument (new_arg, complain);
+	  if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK)
+	    {
+	      tree pack_args = ARGUMENT_PACK_ARGS (new_arg);
+	      for (int i = 0; i < TREE_VEC_LENGTH (pack_args); i++)
+		{
+		  tree& pack_arg = TREE_VEC_ELT (pack_args, i);
+		  if (TYPE_P (pack_arg))
+		    pack_arg = canonicalize_type_argument (pack_arg, complain);
+		}
+	    }
 	}
       if (new_arg == error_mark_node)
 	return error_mark_node;
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
new file mode 100644
index 00000000000..2c8ad40d104
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C
@@ -0,0 +1,52 @@ 
+// PR c++/93907
+// { dg-options -std=gnu++20 }
+
+// This testcase is a variadic version of concepts-using2.C; the only
+// difference is that 'cd' and 'ce' are now variadic concepts.
+
+template <int a> struct c {
+  static constexpr int d = a;
+  typedef c e;
+};
+template <typename> struct f;
+template <typename b> using g = typename f<b>::e;
+struct b;
+template <typename b> struct f { using e = b; };
+template <typename ai> struct m { typedef g<ai> aj; };
+template <typename b> struct n { typedef typename m<b>::aj e; };
+template <typename b> using an = typename n<b>::e;
+template <typename> constexpr bool ao = c<true>::d;
+template <typename> constexpr bool i = c<1>::d;
+template <typename> concept bb = i<b>;
+#ifdef __SIZEOF_INT128__
+using cc = __int128;
+#else
+using cc = long long;
+#endif
+template <typename...> concept cd = bb<cc>;
+template <typename... bt> concept ce = requires { requires cd<bt...>; };
+template <typename bt> concept h = ce<bt>;
+template <typename bt> concept l = h<bt>;
+template <typename> concept cl = ao<b>;
+template <typename b> concept cp = requires(b j) {
+  requires h<an<decltype(j.begin())>>;
+};
+struct o {
+  template <cl b> requires cp<b> auto operator()(b) {}
+};
+template <typename b> using cm = decltype(o{}(b()));
+template <typename bt> concept ct = l<bt>;
+template <typename da> concept dd = ct<cm<da>>;
+template <typename da> concept de = dd<da>;
+struct {
+  template <de da, typename b> void operator()(da, b);
+} di;
+struct p {
+  void begin();
+};
+template <typename> using df = p;
+template <int> void q() {
+  df<int> k;
+  int d;
+  di(k, d);
+}