Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation

From: Michael Welsh Duggan <[email-masked]>
Date: Tue, 28 Mar 2023 10:45:54 -0400

Alan Mackenzie <[email-masked]> writes:

> Hello, Michael.
>
> On Fri, Mar 24, 2023 at 14:58:28 -0400, Michael Welsh Duggan wrote:
>> Alan Mackenzie <[email-masked]> writes:
>
>> > On Wed, Mar 22, 2023 at 11:58:32 -0400, Michael Welsh Duggan wrote:
>> >> Package: cc-mode
>
>> >> Given a concept definition like:
>
>> >>     template <typename T>
>> >>     concept Foo =
>> >>       Bar<T>
>> >>       && requires (T t) {
>> >>         { t + t } -> std::same_as<T>;
>> >>       }
>> >>       && something_else;
>
>> >> This is how it actually indents in cc-mode:
>
>> >>     template <typename T>
>> >>     concept Foo =
>> >>     Bar<T>
>> >>       && requires (T t) {
>> >>       { t + t } -> std::same_as<T>;
>> >>     }
>> >>       && something_else;
>
>> >> I would expect "Bar<T>" to be statement-cont, but it is
>> >> topmost-intro-cont instead.  The indentation of "{ t + t }" isn't offset
>> >> properly with respect to the "requires" clause, and the closing brace is
>> >> way out of line.
>
>> [...]
>
>> > I've made an attempt to improve the indentation of code like your test
>> > case.  Would you please try it out and let me know how well it solves
>> > the problem.  Thanks!
>
>> Better for that test case and a few others.  Here's a few test outliers
>> as indented by cc:
>
> OK.  It is apparent that the former "approximate" solution for concepts
> and requireses wasn't adequate, and that deeper, more rigorous analysis
> was needed.  I've attempted this, and the results are in the patch in
> this post.  They mostly indent your test cases to very close to how you'd
> like them to be.
>
> There's one test in particular that is slightly different, namely:
>
> [ .... ]
>
>> template <typename T>
>> requires 
>>   (requires (T t) { ++t; }
>>    && Baz<T>)
>> int foo();
>
> Here, I haven't managed to align the && under the requires.  Rather it is
> aligned underneath the (.  CC Mode currently has no syntactic symbols for
> this kind of alignment, but there might be one or two line-up functions
> which could do the trick.  (I haven't actually searched for one).
>
>> The latter of these is especially interesting because TAB on the "&&
>> Baz<T>)" section causes an "End of buffer" signal.
>
> I never found out why this was happening.  It's not happening any more.
>
> Here's the patch.  Please try it out on your real code, and let me know
> how well it solves the bug.  Thanks!

Found one strange real code case and one very strange constructed test
case:

  class Chunk {
    template <typename T, typename D>
    requires (OctetLike<T>
      || (std::rank_v<T> == 1 && OctetLike<std::remove_extent_t<T>>))
      Chunk(std::unique_ptr<T, D> && p, length_t size)
        : size_{size}
    {
      auto ptr = p.get();
      data_ = {std::shared_ptr<T>{std::move(p)}, octet_cast(ptr)};
    }
  };

The case above comes from some actual code I have been writing.  The
strange bit is that the Chunk constructor is indented once more than
intended.

  template <typename T>
  requires
    requires (T t) {
  { ++t; }
    }
  && std::is_integral<T>
    int foo();

This has a few problems with it.  The "{ ++t; }" should be indented with
respect to the "requires (T t)" portion.  The "&& std::is_integral<T>"
should be at the same level as that requires.  And "int foo()" is
indented one level too far.

Just in case you didn't know, in this latter case, the first "requires"
introduces an associated constraint, and the second evaluates a
requirement as a requires expression.

https://en.cppreference.com/w/cpp/language/constraints
https://en.cppreference.com/w/cpp/language/requires

-- 
Michael Welsh Duggan
([email-masked])