GNU bug report logs - #62386
CC Mode 5.35.2 (C++//l); C++ concept indentation

Previous Next

Package: cc-mode;

Reported by: Michael Welsh Duggan <mwd <at> md5i.com>

Date: Wed, 22 Mar 2023 15:59:01 UTC

Severity: normal

Done: Alan Mackenzie <acm <at> muc.de>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 62386 in the body.
You can then email your comments to 62386 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Wed, 22 Mar 2023 15:59:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to Michael Welsh Duggan <mwd <at> md5i.com>:
New bug report received and forwarded. Copy sent to bug-cc-mode <at> gnu.org. (Wed, 22 Mar 2023 15:59:01 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Michael Welsh Duggan <mwd <at> md5i.com>
To: bug-gnu-emacs <at> gnu.org
Subject: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Wed, 22 Mar 2023 11:58:32 -0400
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 suspect that this is merely because concepts haven't really been
handled specially outside of recognizing the keyword.  Changing the
"concept" keyword to "int" improves the indentation of "Bar<T>",
demonstrating what I think the indentation should be, and replacing the
"requires" with "[]", making it into lambda syntax, also improves the
indentation.  I think that requires expression should be indented in a
similar manner as lambda expressions are indented.

Thank you!

Emacs  : GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, X toolkit, cairo version 1.16.0)
 of 2023-03-12
Package: CC Mode 5.35.2 (C++//l)
Buffer Style: Pharos
c-emacs-features: (pps-extended-state col-0-paren posix-char-classes gen-string-delim gen-comment-delim syntax-properties category-properties 1-bit)

current state:
==============
(setq
 c-basic-offset 2
 c-comment-only-line-offset 0
 c-indent-comment-alist '((anchored-comment column . 0) (end-block space . 1)
                          (cpp-end-block space . 2))
 c-indent-comments-syntactically-p nil
 c-block-comment-prefix ""
 c-comment-prefix-regexp '((pike-mode . "//+!?\\|\\**") (awk-mode . "#+")
                           (other . "//+\\|\\**"))
 c-doc-comment-style '((java-mode . javadoc) (pike-mode . autodoc)
                       (c-mode . gtkdoc) (c++-mode . gtkdoc))
 c-cleanup-list '(scope-operator)
 c-hanging-braces-alist '((brace-list-open) (brace-entry-open)
                          (statement-cont) (substatement-open after)
                          (block-close . c-snug-do-while)
                          (extern-lang-open after) (namespace-open after)
                          (module-open after) (composition-open after)
                          (inexpr-class-open after)
                          (inexpr-class-close before) (arglist-cont-nonempty))
 c-hanging-colons-alist nil
 c-hanging-semi&comma-criteria '(c-semi&comma-inside-parenlist)
 c-backslash-column 48
 c-backslash-max-column 72
 c-special-indent-hook nil
 c-label-minimum-indentation 1
 c-offsets-alist '((inexpr-class . +)
                   (inexpr-statement . +)
                   (lambda-intro-cont . +)
                   (inlambda . 0)
                   (template-args-cont c-lineup-template-args +)
                   (incomposition . +)
                   (inmodule . +)
                   (innamespace . 0)
                   (inextern-lang . 0)
                   (composition-close . 0)
                   (module-close . 0)
                   (namespace-close . 0)
                   (extern-lang-close . 0)
                   (composition-open . 0)
                   (module-open . 0)
                   (namespace-open . 0)
                   (extern-lang-open . 0)
                   (objc-method-call-cont
                    c-lineup-ObjC-method-call-colons
                    c-lineup-ObjC-method-call
                    +
                    )
                   (objc-method-args-cont . c-lineup-ObjC-method-args)
                   (objc-method-intro . [0])
                   (friend . 0)
                   (cpp-define-intro c-lineup-cpp-define +)
                   (cpp-macro-cont . +)
                   (cpp-macro . [0])
                   (inclass . +)
                   (stream-op . c-lineup-streamop)
                   (arglist-cont-nonempty
                    c-lineup-gcc-asm-reg
                    c-lineup-arglist
                    )
                   (arglist-cont c-lineup-gcc-asm-reg 0)
                   (comment-intro
                    c-lineup-knr-region-comment
                    c-lineup-comment
                    )
                   (catch-clause . 0)
                   (else-clause . 0)
                   (do-while-closure . 0)
                   (access-label . /)
                   (case-label . *)
                   (substatement . +)
                   (statement-case-intro . *)
                   (statement . 0)
                   (brace-entry-open . 0)
                   (brace-list-entry . 0)
                   (brace-list-close . 0)
                   (block-close . 0)
                   (block-open . 0)
                   (inher-cont . c-lineup-multi-inher)
                   (inher-intro . +)
                   (member-init-cont . c-lineup-multi-inher)
                   (member-init-intro . +)
                   (annotation-var-cont . +)
                   (annotation-top-cont . 0)
                   (topmost-intro . 0)
                   (knr-argdecl . 0)
                   (func-decl-cont . +)
                   (inline-close . 0)
                   (class-close . 0)
                   (class-open . 0)
                   (defun-block-intro . +)
                   (defun-close . 0)
                   (defun-open . 0)
                   (c . c-lineup-C-comments)
                   (string . c-lineup-dont-change)
                   (topmost-intro-cont . c-lineup-topmost-intro-cont)
                   (brace-list-intro . +)
                   (brace-list-open . 0)
                   (inline-open . 0)
                   (arglist-close . +)
                   (arglist-intro . +)
                   (statement-cont . c-lineup-math)
                   (statement-case-open . *)
                   (label . *)
                   (substatement-label . 2)
                   (substatement-open . 0)
                   (knr-argdecl-intro . +)
                   (statement-block-intro . +)
                   )
 c-buffer-is-cc-mode 'c++-mode
 c-tab-always-indent t
 c-syntactic-indentation t
 c-syntactic-indentation-in-macros t
 c-ignore-auto-fill '(string cpp code)
 c-auto-align-backslashes t
 c-backspace-function 'backward-delete-char-untabify
 c-delete-function 'delete-char
 c-electric-pound-behavior nil
 c-default-style '((java-mode . "java") (awk-mode . "awk") (other . "gnu"))
 c-enable-xemacs-performance-kludge-p nil
 c-old-style-variable-behavior nil
 defun-prompt-regexp nil
 tab-width 8
 comment-column 32
 parse-sexp-ignore-comments t
 parse-sexp-lookup-properties t
 auto-fill-function nil
 comment-multi-line t
 comment-start-skip "\\(?://+\\|/\\*+\\)\\s *"
 fill-prefix nil
 fill-column 70
 paragraph-start "[ 	]*\\(//+\\|\\**\\)[ 	]*$\\|^\f"
 adaptive-fill-mode t
 adaptive-fill-regexp "[ 	]*\\(//+\\|\\**\\)[ 	]*\\([ 	]*\\([-–!|#%;>*·•‣⁃◦]+[ 	]*\\)*\\)"
 )
-- 
Michael Welsh Duggan
(md5i <at> md5i.com)




Information forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Fri, 24 Mar 2023 17:24:02 GMT) Full text and rfc822 format available.

Message #8 received at 62386 <at> debbugs.gnu.org (full text, mbox):

From: Alan Mackenzie <acm <at> muc.de>
To: Michael Welsh Duggan <mwd <at> md5i.com>
Cc: acm <at> muc.de, 62386 <at> debbugs.gnu.org
Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Fri, 24 Mar 2023 17:23:00 +0000
Hello, Michael,

Thanks for taking the trouble to report this bug.

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.

Concepts can only occur at the top level, so it seemed logical to use
topmost-intro-cont and friends.  In the current indentation, just about
everything is anchored on the start of the statement (i.e. the
"template" keyword) so that's why the closing brace is in column 0.

> I suspect that this is merely because concepts haven't really been
> handled specially outside of recognizing the keyword.

Hah!  The patch file for the implementation of concepts and requireses
was over 600 lines long.  ;-)

Your bug report is in fact the first feedback I've had about this
feature since it was published ~6 months ago.  Most of the sample source
I used to program it came from https://en.cppreference.com, which was
formatted in a particular fashion.  So I missed the points which you've
just raised.

> Changing the "concept" keyword to "int" improves the indentation of
> "Bar<T>", demonstrating what I think the indentation should be, and
> replacing the "requires" with "[]", making it into lambda syntax, also
> improves the indentation.  I think that requires expression should be
> indented in a similar manner as lambda expressions are indented.

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!


diff -r 300ebf19cd62 cc-align.el
--- a/cc-align.el	Fri Feb 17 08:58:11 2023 +0000
+++ b/cc-align.el	Fri Mar 24 17:04:07 2023 +0000
@@ -93,7 +93,7 @@
 			       (c-point 'eol) t)
       (beginning-of-line)
       (c-backward-syntactic-ws (c-langelem-pos langelem))
-      (if (and (memq (char-before) '(?} ?,))
+      (if (and (memq (char-before) '(?} ?, ?=))
 	       (not (and c-overloadable-operators-regexp
 			 (c-after-special-operator-id))))
 	  c-basic-offset))))
diff -r 300ebf19cd62 cc-engine.el
--- a/cc-engine.el	Fri Feb 17 08:58:11 2023 +0000
+++ b/cc-engine.el	Fri Mar 24 17:04:07 2023 +0000
@@ -13223,6 +13223,28 @@
 	   (t nil)))
       (goto-char here))))
 
+(defun c-looking-at-concept (limit)
+  ;; Are we currently at the start of a concept construct?  I.e. at the
+  ;; "template" keyword followed by the construct?  If so, we return the
+  ;; position of the first constraint expression following the "=" sign,
+  ;; otherwise we return nil.  LIMIT is a forward search limit
+  (save-excursion
+    (and (looking-at c-pre-concept-<>-key)
+	 (goto-char (match-end 1))
+	 (progn (c-forward-syntactic-ws limit)
+		(eq (char-after) ?<))
+	 (let ((c-parse-and-markup-<>-arglists t)
+	       c-restricted-<>-arglists)
+	   (c-forward-<>-arglist nil))
+	 (progn (c-forward-syntactic-ws limit)
+		(looking-at c-equals-nontype-decl-key)) ; "concept"
+	 (goto-char (match-end 0))
+	 (c-syntactic-re-search-forward
+	  "=" limit t t)
+	 (goto-char (match-end 0))
+	 (progn (c-forward-syntactic-ws limit)
+		(point)))))
+
 (defun c-looking-at-inexpr-block (lim containing-sexp &optional check-at-end)
   ;; Return non-nil if we're looking at the beginning of a block
   ;; inside an expression.  The value returned is actually a cons of
@@ -15310,6 +15332,15 @@
 	      (c-add-syntax 'defun-close (point))
 	    (c-add-syntax 'inline-close (point))))
 
+	 ;; CASE 16G: Do we have the closing brace of a "requires" clause
+	 ;; of a C++20 "concept"?
+	 ((save-excursion
+	    (and (eq (c-beginning-of-statement-1 lim nil nil t) 'same)
+		 (setq placeholder (c-looking-at-concept containing-sexp))))
+	  (goto-char placeholder)
+	  (back-to-indentation)
+	  (c-add-stmt-syntax 'defun-close nil t lim paren-state))
+
 	 ;; CASE 16F: Can be a defun-close of a function declared
 	 ;; in a statement block, e.g. in Pike or when using gcc
 	 ;; extensions, but watch out for macros followed by
@@ -15460,6 +15491,17 @@
 	  (if (eq char-after-ip ?{)
 	      (c-add-syntax 'block-open)))
 
+	 ;; CASE 17J: first "statement" inside a C++20 requires
+	 ;; "function".
+	 ((save-excursion
+	    (and
+	     (goto-char containing-sexp)
+	     (eq (c-beginning-of-statement-1 lim nil nil t) 'same)
+	     (setq placeholder (c-looking-at-concept lim))))
+	  (goto-char placeholder)
+	  (back-to-indentation)
+	  (c-add-syntax 'defun-block-intro (point)))
+
 	 ;; CASE 17F: first statement in an inline, or first
 	 ;; statement in a top-level defun. we can tell this is it
 	 ;; if there are no enclosing braces that haven't been


> Thank you!

-- 
Alan Mackenzie (Nuremberg, Germany).






Information forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Fri, 24 Mar 2023 18:59:02 GMT) Full text and rfc822 format available.

Message #11 received at 62386 <at> debbugs.gnu.org (full text, mbox):

From: Michael Welsh Duggan <mwd <at> md5i.com>
To: Alan Mackenzie <acm <at> muc.de>
Cc: Michael Welsh Duggan <mwd <at> md5i.com>, 62386 <at> debbugs.gnu.org
Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Fri, 24 Mar 2023 14:58:28 -0400
Alan Mackenzie <acm <at> muc.de> 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:

template <typename T>
concept Foo2 =
requires (T t) {
  *t -> std::same_as<int>; 
  *(t + t) -> std::same_as<int>; 
};
  
template <typename T>
concept Foo5 = 
  Foo<T>
  && requires (T t)
{
    { t++; }
  };

and how I would expect them to be indented:

template <typename T>
concept Foo2 =
  requires (T t) {
    *t -> std::same_as<int>; 
    *(t + t) -> std::same_as<int>; 
  };
  
template <typename T>
concept Foo5 = 
  Foo<T>
  && requires (T t)
  {
    { t++; }
  };

Here's some requires clause cases outside of concepts:

template <typename T>
requires Bar<T>
&& Baz<T>
int foo();

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

and how I would expect them to be indented:

template <typename T>
requires Bar<T>
  && Baz<T>
int foo();

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

The latter of these is especially interesting because TAB on the "&&
Baz<T>)" section causes an "End of buffer" signal.

-- 
Michael Welsh Duggan
(md5i <at> md5i.com)




Information forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Mon, 27 Mar 2023 18:38:02 GMT) Full text and rfc822 format available.

Message #14 received at 62386 <at> debbugs.gnu.org (full text, mbox):

From: Alan Mackenzie <acm <at> muc.de>
To: Michael Welsh Duggan <mwd <at> md5i.com>
Cc: acm <at> muc.de, 62386 <at> debbugs.gnu.org
Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Mon, 27 Mar 2023 18:37:17 +0000
Hello, Michael.

On Fri, Mar 24, 2023 at 14:58:28 -0400, Michael Welsh Duggan wrote:
> Alan Mackenzie <acm <at> muc.de> 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!



diff -r 300ebf19cd62 cc-align.el
--- a/cc-align.el	Fri Feb 17 08:58:11 2023 +0000
+++ b/cc-align.el	Mon Mar 27 18:21:42 2023 +0000
@@ -87,16 +87,22 @@
 statement-cont.)
 
 Works with: topmost-intro-cont."
-  (save-excursion
-    (beginning-of-line)
-    (unless (re-search-forward c-fun-name-substitute-key
-			       (c-point 'eol) t)
+  (let ((here (point))
+	conpos)
+    (save-excursion
       (beginning-of-line)
       (c-backward-syntactic-ws (c-langelem-pos langelem))
-      (if (and (memq (char-before) '(?} ?,))
-	       (not (and c-overloadable-operators-regexp
-			 (c-after-special-operator-id))))
-	  c-basic-offset))))
+      (cond
+       ((and (memq (char-before) '(?} ?, ?=))
+	     (not (and c-overloadable-operators-regexp
+		       (c-after-special-operator-id))))
+	c-basic-offset)
+       ((and
+	 (goto-char (c-langelem-pos langelem))
+	 (setq conpos (car (c-looking-at-concept)))
+	 (> here (c-point 'eol conpos)))
+	c-basic-offset)
+       (t 0)))))
 
 (defun c-lineup-gnu-DEFUN-intro-cont (langelem)
   "Line up the continuation lines of a DEFUN macro in the Emacs C source.
diff -r 300ebf19cd62 cc-engine.el
--- a/cc-engine.el	Fri Feb 17 08:58:11 2023 +0000
+++ b/cc-engine.el	Mon Mar 27 18:21:42 2023 +0000
@@ -9683,6 +9683,9 @@
   ;;
   ;; Note that this function is incomplete, handling only those cases expected
   ;; to be common in a C++20 requires clause.
+  ;;
+  ;; Note also that (...) is not recognised as a primary expression if the next
+  ;; token is an open brace.
   (let ((here (point))
 	(c-restricted-<>-arglists t)
 	(c-parse-and-markup-<>-arglists nil)
@@ -9696,7 +9699,8 @@
 	  (and (c-go-list-forward (point) limit)
 	       (eq (char-before) ?\))
 	       (progn (c-forward-syntactic-ws limit)
-		      t)))
+		      (not (eq (char-after) ?{))
+		      )))
 	 ((c-forward-over-compound-identifier)
 	  (c-forward-syntactic-ws limit)
 	  (while (cond
@@ -9744,6 +9748,7 @@
 	   (setq final-point (point))
 	   (while
 	       (and (looking-at "\\(?:&&\\|||\\)")
+		    (<= (match-end 0) limit)
 		    (progn (goto-char (match-end 0))
 			   (c-forward-syntactic-ws limit)
 			   (and (< (point) limit)
@@ -13223,6 +13228,65 @@
 	   (t nil)))
       (goto-char here))))
 
+(defun c-looking-at-concept (&optional limit)
+  ;; Are we currently at the start of a concept construct?  I.e. at the
+  ;; "template" keyword followed by the construct?  If so, we return a cons of
+  ;; the position of "concept" and the position of the first constraint
+  ;; expression following the "=" sign, otherwise we return nil.  LIMIT is a
+  ;; forward search limit
+  (save-excursion
+    (let (conpos)
+      (and (looking-at c-pre-concept-<>-key)
+	   (goto-char (match-end 1))
+	   (progn (c-forward-syntactic-ws limit)
+		  (eq (char-after) ?<))
+	   (let ((c-parse-and-markup-<>-arglists t)
+		 c-restricted-<>-arglists)
+	     (c-forward-<>-arglist nil))
+	   (progn (c-forward-syntactic-ws limit)
+		  (looking-at c-equals-nontype-decl-key)) ; "concept"
+	   (setq conpos (match-beginning 0))
+	   (goto-char (match-end 0))
+	   (c-syntactic-re-search-forward
+	    "=" limit t t)
+	   (goto-char (match-end 0))
+	   (progn (c-forward-syntactic-ws limit)
+		  (cons conpos (point)))))))
+
+(defun c-in-or-at-end-of-requires (&optional pos)
+  ;; Is POS (default POINT) in a C++ "requires" clause or at the end of one?
+  ;; If so return a cons (POSITION . END) where POSITION is that of the
+  ;; "requires" keyword and END is t if POS is at the end of the clause,
+  ;; otherwise nil.  "End of the clause" means just after the last non
+  ;; syntactic WS on the line where the clause ends.
+  (save-excursion
+    (if pos (goto-char pos) (setq pos (point)))
+    (let ((limit (max (- (point) 2000) (point-min)))
+	  found-req req-pos found-clause res
+	  )
+      (while
+	  (progn
+	    (while
+		(and
+		 (setq found-req (re-search-backward
+				  c-fun-name-substitute-key limit t)) ; Fast!
+		 (not (setq found-req (not (c-in-literal)))))) ; Slow!
+	    (setq req-pos (point))
+	    (cond
+	     ((not found-req)
+	      nil)
+	     ((progn (setq found-clause (c-forward-c++-requires-clause))
+		     nil))
+	     ((and found-clause (>= (point) pos))
+	      (c-backward-syntactic-ws)
+	      (setq res (cons req-pos (eq (point) pos)))
+	      nil)
+	     (found-clause
+	      (c-go-up-list-backward req-pos limit))
+	     (t (goto-char req-pos)
+		t))))
+      res)))
+
 (defun c-looking-at-inexpr-block (lim containing-sexp &optional check-at-end)
   ;; Return non-nil if we're looking at the beginning of a block
   ;; inside an expression.  The value returned is actually a cons of
@@ -13419,6 +13483,20 @@
 	 (looking-at c-pre-lambda-tokens-re)))
    (not (c-in-literal))))
 
+(defun c-c++-vsemi-p (&optional pos)
+  ;; C++ Only - Is there a "virtual semicolon" at POS or point?
+  ;; (See cc-defs.el for full details of "virtual semicolons".)
+  ;;
+  ;; This is true either when point is at the last non syntactic WS position
+  ;; on the line, and either there is a "macro with semicolon" just before it
+  ;; (see `c-at-macro-vsemi-p') or there is a "requires" clause which ends
+  ;; there.
+  (let (res)
+    (cond
+     ((setq res (c-in-or-at-end-of-requires pos))
+      (and res (cdr res)))
+     ((c-at-macro-vsemi-p)))))
+
 (defun c-at-macro-vsemi-p (&optional pos)
   ;; Is there a "virtual semicolon" at POS or point?
   ;; (See cc-defs.el for full details of "virtual semicolons".)
@@ -14385,6 +14463,32 @@
 				containing-decl-start
 				containing-decl-kwd))
 
+	   ;; CASE 5A.7: "defun" open in a requires expression.
+	   ((save-excursion
+	      (goto-char indent-point)
+	      (c-backward-syntactic-ws lim)
+	      (and (or (not (eq (char-before) ?\)))
+		       (c-go-list-backward nil lim))
+		   (progn (c-backward-syntactic-ws lim)
+			  (zerop (c-backward-token-2 nil nil lim)))
+		   (looking-at c-fun-name-substitute-key)
+		   (setq placeholder (point))))
+	    (goto-char placeholder)
+	    (back-to-indentation)
+	    (c-add-syntax 'defun-open (point)))
+
+	   ;; CASE 5A.6: "defun" open in concept.
+	   ((save-excursion
+	      (goto-char indent-point)
+	      (skip-chars-forward " \t")
+	      (and (eq (char-after) ?{)
+		   (eq (c-beginning-of-statement-1 lim) 'same)
+		   (setq placeholder
+			 (cdr (c-looking-at-concept indent-point)))))
+	    (goto-char placeholder)
+	    (back-to-indentation)
+	    (c-add-syntax 'defun-open (point)))
+
 	   ;; CASE 5A.5: ordinary defun open
 	   (t
 	    (save-excursion
@@ -14660,17 +14764,31 @@
 	   ;; similar).
 	   ((and c-equals-nontype-decl-key
 		 (save-excursion
-		   (prog1
-		       (and (zerop (c-backward-token-2 1 nil lim))
-			    (looking-at c-operator-re)
-			    (equal (match-string 0) "=")
-			    (zerop (c-backward-token-2 1 nil lim))
-			    (looking-at c-symbol-start)
-			    (not (looking-at c-keywords-regexp))
-			    (zerop (c-backward-token-2 1 nil lim))
-			    (looking-at c-equals-nontype-decl-key)
-			    (eq (c-beginning-of-statement-1 lim) 'same))
-		     (setq placeholder (point)))))
+		   (and
+		    (eq (c-beginning-of-statement-1 lim) 'same)
+		    (setq placeholder (point))
+		    (< (point) indent-point)
+		    (re-search-forward c-equals-nontype-decl-key
+				       indent-point t)
+		    (progn (c-forward-syntactic-ws indent-point)
+			   (looking-at c-symbol-start))
+		    (not (looking-at c-keywords-regexp))
+		    (zerop (c-forward-token-2))
+		    (< (point) indent-point)
+		    (looking-at c-operator-re)
+		    (prog1 (equal (match-string 0) "=")
+		      ;; (c-forward-token-2 nil nil indent-point)
+		      (c-forward-over-token-and-ws))
+		    (progn
+		      (while
+			  (and (< (point) indent-point)
+			       (c-forward-primary-expression indent-point)
+			       (<= (point) indent-point)
+			       (looking-at "&&\\|||")
+			       (<= (point) indent-point))
+			(goto-char (match-end 0))
+			(c-forward-syntactic-ws indent-point))
+		      (>= (point) indent-point)))))
 	    (goto-char placeholder)
 	    (c-add-stmt-syntax 'topmost-intro-cont nil nil containing-sexp
 			       paren-state))
@@ -14912,6 +15030,10 @@
 		 (goto-char placeholder)))
 	  (c-add-syntax 'annotation-top-cont (c-point 'boi tmp-pos2)))
 
+	 ;; CASE 5T: We are inside or after a C++ "requires" clause.
+	 ((setq placeholder (car-safe (c-in-or-at-end-of-requires)))
+	  (c-add-syntax 'statement-cont placeholder))
+
 	 ;; CASE 5M: we are at a topmost continuation line
 	 (t
 	  (c-beginning-of-statement-1
@@ -14927,6 +15049,14 @@
 	  (c-add-syntax 'topmost-intro-cont (c-point 'boi)))
 	 ))
 
+       ;; CASE 20: A C++ requires sub-clause.
+       ((setq placeholder (car (c-in-or-at-end-of-requires indent-point)))
+	(c-add-syntax
+	 (if (eq char-after-ip ?{)
+	     'substatement-open
+	   'substatement)
+	 (c-point 'boi placeholder)))
+
        ;; ((Old) CASE 6 has been removed.)
        ;; CASE 6: line is within a C11 _Generic expression.
        ((and c-generic-key
@@ -15310,6 +15440,19 @@
 	      (c-add-syntax 'defun-close (point))
 	    (c-add-syntax 'inline-close (point))))
 
+	 ;; CASE 16G: Do we have the closing brace of a "requires" clause
+	 ;; of a C++20 "concept"?
+	 ((save-excursion
+	    (c-backward-syntactic-ws lim)
+	    (and (or (not (eq (char-before) ?\)))
+		     (c-go-list-backward nil lim))
+		 (progn (c-backward-syntactic-ws lim)
+			(zerop (c-backward-token-2 nil nil lim)))
+		 (looking-at c-fun-name-substitute-key)))
+	  (goto-char containing-sexp)
+	  (back-to-indentation)
+	  (c-add-stmt-syntax 'defun-close nil t lim paren-state))
+
 	 ;; CASE 16F: Can be a defun-close of a function declared
 	 ;; in a statement block, e.g. in Pike or when using gcc
 	 ;; extensions, but watch out for macros followed by
@@ -15460,6 +15603,20 @@
 	  (if (eq char-after-ip ?{)
 	      (c-add-syntax 'block-open)))
 
+	 ;; CASE 17J: first "statement" inside a C++20 requires
+	 ;; "function".
+	 ((save-excursion
+	    (goto-char containing-sexp)
+	    (c-backward-syntactic-ws lim)
+	    (and (or (not (eq (char-before) ?\)))
+		     (c-go-list-backward nil lim))
+		 (progn (c-backward-syntactic-ws lim)
+			(zerop (c-backward-token-2 nil nil lim)))
+		 (looking-at c-fun-name-substitute-key)))
+	  (goto-char containing-sexp)
+	  (back-to-indentation)
+	  (c-add-syntax 'defun-block-intro (point)))
+
 	 ;; CASE 17F: first statement in an inline, or first
 	 ;; statement in a top-level defun. we can tell this is it
 	 ;; if there are no enclosing braces that haven't been
diff -r 300ebf19cd62 cc-langs.el
--- a/cc-langs.el	Fri Feb 17 08:58:11 2023 +0000
+++ b/cc-langs.el	Mon Mar 27 18:21:42 2023 +0000
@@ -571,7 +571,8 @@
 point), and returns nil or t.  This variable contains nil for languages which
 don't have EOL terminated statements. "
   t nil
-  (c c++ objc) 'c-at-macro-vsemi-p
+  (c objc) 'c-at-macro-vsemi-p
+  c++ 'c-c++-vsemi-p
   awk 'c-awk-at-vsemi-p)
 (c-lang-defvar c-at-vsemi-p-fn (c-lang-const c-at-vsemi-p-fn))
 


> -- 
> Michael Welsh Duggan
> (md5i <at> md5i.com)

-- 
Alan Mackenzie (Nuremberg, Germany).




Information forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Tue, 28 Mar 2023 14:47:01 GMT) Full text and rfc822 format available.

Message #17 received at 62386 <at> debbugs.gnu.org (full text, mbox):

From: Michael Welsh Duggan <mwd <at> md5i.com>
To: Alan Mackenzie <acm <at> muc.de>
Cc: Michael Welsh Duggan <mwd <at> md5i.com>, 62386 <at> debbugs.gnu.org
Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Tue, 28 Mar 2023 10:45:54 -0400
Alan Mackenzie <acm <at> muc.de> writes:

> Hello, Michael.
>
> On Fri, Mar 24, 2023 at 14:58:28 -0400, Michael Welsh Duggan wrote:
>> Alan Mackenzie <acm <at> muc.de> 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
(md5i <at> md5i.com)




Information forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Tue, 28 Mar 2023 14:49:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Mon, 03 Apr 2023 20:12:02 GMT) Full text and rfc822 format available.

Message #23 received at 62386 <at> debbugs.gnu.org (full text, mbox):

From: Alan Mackenzie <acm <at> muc.de>
To: Michael Welsh Duggan <mwd <at> md5i.com>
Cc: acm <at> muc.de, 62386 <at> debbugs.gnu.org
Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Mon, 3 Apr 2023 20:10:54 +0000
Hello, Michael.

On Tue, Mar 28, 2023 at 10:45:54 -0400, Michael Welsh Duggan wrote:
> 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();

I think I've now got these corrected.  The coding was much more difficult
than I'd anticipated, with me spending several days in total confusion.
The last thing which threw me off was the realisation that the
expressions in the concept clause need not be "primary" expressions, i.e.
something like !foo<T> is valid.  This is not the case with requires
clauses, where only "primary" expressions are permitted.

> 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.

Thanks, you've just got to love the C++ Standards Committee.  ;-)

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

Thanks again, I've had those pages in my web browser cache for quite some
time, now.

Here's the latest version of the patch.  Please do the usual with it, and
let me know how it goes.  Thanks!



diff -r 300ebf19cd62 cc-engine.el
--- a/cc-engine.el	Fri Feb 17 08:58:11 2023 +0000
+++ b/cc-engine.el	Mon Apr 03 19:49:17 2023 +0000
@@ -9476,19 +9476,24 @@
        (setq ,ps (cdr ,ps)))))
 
 (defun c-forward-over-compound-identifier ()
-  ;; Go over a possibly compound identifier, such as C++'s Foo::Bar::Baz,
-  ;; returning that identifier (with any syntactic WS removed).  Return nil if
-  ;; we're not at an identifier.
-  (when (c-on-identifier)
+  ;; Go over a possibly compound identifier (but not any following
+  ;; whitespace), such as C++'s Foo::Bar::Baz, returning that identifier (with
+  ;; any syntactic WS removed).  Return nil if we're not at an identifier, in
+  ;; which case point is not moved.
+  (when
+      (eq (c-on-identifier)
+	  (point))
     (let ((consolidated "") (consolidated-:: "")
-	  start end)
+	  (here (point))
+	  start end end-token)
       (while
        (progn
 	 (setq start (point))
 	 (c-forward-over-token)
 	 (setq consolidated
 	       (concat consolidated-::
-		       (buffer-substring-no-properties start (point))))
+		       (buffer-substring-no-properties start (point)))
+	       end-token (point))
 	 (c-forward-syntactic-ws)
 	 (and c-opt-identifier-concat-key
 	      (looking-at c-opt-identifier-concat-key)
@@ -9503,7 +9508,9 @@
 		       (concat consolidated
 			       (buffer-substring-no-properties start end))))))))
       (if (equal consolidated "")
-	  nil
+	  (progn (goto-char here)
+		 nil)
+	(goto-char end-token)
 	consolidated))))
 
 (defun c-back-over-compound-identifier ()
@@ -9676,13 +9683,16 @@
 
 ;; Handling of large scale constructs like statements and declarations.
 
-(defun c-forward-primary-expression (&optional limit)
-  ;; Go over the primary expression (if any) at point, moving to the next
-  ;; token and return non-nil.  If we're not at a primary expression leave
-  ;; point unchanged and return nil.
+(defun c-forward-primary-expression (&optional limit stop-at-end)
+  ;; Go over the primary expression (if any) at point, and unless STOP-AT-END
+  ;; is non-nil, move to the next token then return non-nil.  If we're not at
+  ;; a primary expression leave point unchanged and return nil.
   ;;
   ;; Note that this function is incomplete, handling only those cases expected
   ;; to be common in a C++20 requires clause.
+  ;;
+  ;; Note also that (...) is not recognised as a primary expression if the
+  ;; next token is an open brace.
   (let ((here (point))
 	(c-restricted-<>-arglists t)
 	(c-parse-and-markup-<>-arglists nil)
@@ -9690,28 +9700,38 @@
     (if	(cond
 	 ((looking-at c-constant-key)
 	  (goto-char (match-end 1))
-	  (c-forward-syntactic-ws limit)
+	  (unless stop-at-end (c-forward-syntactic-ws limit))
 	  t)
 	 ((eq (char-after) ?\()
 	  (and (c-go-list-forward (point) limit)
 	       (eq (char-before) ?\))
-	       (progn (c-forward-syntactic-ws limit)
-		      t)))
+	       (let ((after-paren (point)))
+		 (c-forward-syntactic-ws limit)
+		 (prog1
+		     (not (eq (char-after) ?{))
+		   (when stop-at-end
+		     (goto-char after-paren))))))
 	 ((c-forward-over-compound-identifier)
-	  (c-forward-syntactic-ws limit)
-	  (while (cond
-		  ((looking-at "<")
-		   (prog1
-		       (c-forward-<>-arglist nil)
-		     (c-forward-syntactic-ws limit)))
-		  ((looking-at c-opt-identifier-concat-key)
-		   (and
-		    (zerop (c-forward-token-2 1 nil limit))
-		    (prog1
-			(c-forward-over-compound-identifier)
-		      (c-forward-syntactic-ws limit))))))
-	  t)
-	 ((looking-at c-fun-name-substitute-key) ; "requires"
+	  (let ((after-id (point)))
+	    (c-forward-syntactic-ws limit)
+	    (while (cond
+		    ((and
+		      (looking-at "<")
+		      (prog1
+			  (and
+			   (c-forward-<>-arglist nil)
+			   (setq after-id (point)))))
+		     (c-forward-syntactic-ws limit))
+		    ((looking-at c-opt-identifier-concat-key)
+		     (and
+		      (zerop (c-forward-token-2 1 nil limit))
+		      (prog1
+			  (c-forward-over-compound-identifier)
+			(c-forward-syntactic-ws limit))))))
+	    (goto-char after-id)))
+	 ((and
+	   (looking-at c-fun-name-substitute-key) ; "requires"
+	   (not (eq (char-after (match-end 0)) ?_)))
 	  (goto-char (match-end 1))
 	  (c-forward-syntactic-ws limit)
 	  (and
@@ -9724,36 +9744,47 @@
 	   (and (c-go-list-forward (point) limit)
 		(eq (char-before) ?}))
 	   (progn
-	     (c-forward-syntactic-ws limit)
+	     (unless stop-at-end (c-forward-syntactic-ws limit))
 	     t))))
 	t
       (goto-char here)
       nil)))
 
-(defun c-forward-c++-requires-clause (&optional limit)
-  ;; Point is at the keyword "requires".  Move forward over the requires
-  ;; clause to the next token after it and return non-nil.  If there is no
-  ;; valid requires clause at point, leave point unmoved and return nil.
+(defun c-forward-constraint-clause (&optional limit stop-at-end)
+  ;; Point is at the putative start of a constraint clause.  Move to its end
+  ;; (when STOP-AT-END is non-zero) or the token after that (otherwise) and
+  ;; return non-nil.  Return nil without moving if we fail to find a
+  ;; constraint.
   (let ((here (point))
 	final-point)
     (or limit (setq limit (point-max)))
-    (if (and
-	 (zerop (c-forward-token-2 1 nil limit)) ; over "requires".
-	 (prog1
-	     (c-forward-primary-expression limit)
-	   (setq final-point (point))
-	   (while
-	       (and (looking-at "\\(?:&&\\|||\\)")
-		    (progn (goto-char (match-end 0))
-			   (c-forward-syntactic-ws limit)
-			   (and (< (point) limit)
-				(c-forward-primary-expression limit))))
-	     (setq final-point (point)))))
-	(progn (goto-char final-point)
-	       t)
+    (if (c-forward-primary-expression limit t)
+	(progn
+	  (setq final-point (point))
+	  (c-forward-syntactic-ws limit)
+	  (while
+	      (and (looking-at "\\(?:&&\\|||\\)")
+		   (<= (match-end 0) limit)
+		   (progn (goto-char (match-end 0))
+			  (c-forward-syntactic-ws limit)
+			  (and (<= (point) limit)))
+		   (c-forward-primary-expression limit t)
+		   (setq final-point (point))))
+	  (goto-char final-point)
+	  (or stop-at-end (c-forward-syntactic-ws limit))
+	  t)
       (goto-char here)
       nil)))
 
+(defun c-forward-c++-requires-clause (&optional limit stop-at-end)
+  ;; Point is at the keyword "requires".  Move forward over the requires
+  ;; clause to its end (if STOP-AT-END is non-nil) or the next token after it
+  ;; (otherwise) and return non-nil.  If there is no valid requires clause at
+  ;; point, leave point unmoved and return nil.
+  (or limit (setq limit (point-max)))
+  (and (zerop (c-forward-token-2))	; over "requires".
+       (c-forward-constraint-clause limit stop-at-end)))
+
 (defun c-forward-decl-arglist (not-top id-in-parens &optional limit)
   ;; Point is at an open parenthesis, assumed to be the arglist of a function
   ;; declaration.  Move over this arglist and following syntactic whitespace,
@@ -9955,7 +9986,9 @@
 		   ((looking-at c-type-decl-suffix-key)
 		    (cond
 		     ((save-match-data
-			(looking-at c-fun-name-substitute-key))
+			(and
+			 (looking-at c-fun-name-substitute-key)
+			 (not (eq (char-after (match-end 0)) ?_))))
 		      (c-forward-c++-requires-clause))
 		     ((eq (char-after) ?\()
 		      (if (c-forward-decl-arglist not-top decorated limit)
@@ -10407,7 +10440,9 @@
 	      (when (and (c-major-mode-is 'c++-mode)
 			 (c-keyword-member kwd-sym 'c-<>-sexp-kwds)
 			 (save-match-data
-			   (looking-at c-fun-name-substitute-key)))
+			   (and
+			    (looking-at c-fun-name-substitute-key)
+			    (not (eq (char-after (match-end 0)) ?_)))))
 		(c-forward-c++-requires-clause))
 	      (setq kwd-clause-end (point))))
 	   ((and c-opt-cpp-prefix
@@ -10757,7 +10792,9 @@
 		     ((save-match-data (looking-at "\\s("))
 		      (c-safe (c-forward-sexp 1) t))
 		     ((save-match-data
-			(looking-at c-fun-name-substitute-key)) ; C++ requires
+			(and
+			 (looking-at c-fun-name-substitute-key)
+			 (not (eq (char-after (match-end 0)) ?_)))) ; C++ requires
 		      (c-forward-c++-requires-clause))
 		     (t (goto-char (match-end 1))
 			t))
@@ -12877,7 +12914,9 @@
 		       in-paren 'in-paren))
 		((looking-at c-pre-brace-non-bracelist-key)
 		 (setq braceassignp nil))
-		((looking-at c-fun-name-substitute-key)
+		((and
+		  (looking-at c-fun-name-substitute-key)
+		  (not (eq (char-after (match-end 0)) ?_)))
 		 (setq braceassignp nil))
 		((looking-at c-return-key))
 		((and (looking-at c-symbol-start)
@@ -12892,7 +12931,8 @@
 		 ;; Have we a requires with a parenthesis list?
 		 (when (save-excursion
 			 (and (zerop (c-backward-token-2 1 nil lim))
-			      (looking-at c-fun-name-substitute-key)))
+			      (looking-at c-fun-name-substitute-key)
+			      (not (eq (char-after (match-end 0)) ?_))))
 		   (setq braceassignp nil))
 		 nil)
 		(t nil))
@@ -13223,6 +13263,120 @@
 	   (t nil)))
       (goto-char here))))
 
+(defun c-forward-concept-fragment (&optional limit stop-at-end)
+  ;; Are we currently at the "concept" keyword in a concept construct?  If so
+  ;; we return the position of the first constraint expression following the
+  ;; "=" sign and move forward over the constraint.  Otherwise we return nil.
+  ;; LIMIT is a forward search limit.
+  (let ((here (point)))
+    (if
+	(and
+	 (looking-at c-equals-nontype-decl-key) ; "concept"
+	 (goto-char (match-end 0))
+	 (progn (c-forward-syntactic-ws limit)
+		(not (looking-at c-keywords-regexp)))
+	 (looking-at c-identifier-key)
+	 (goto-char (match-end 0))
+	 (progn (c-forward-syntactic-ws limit)
+		(looking-at c-operator-re))
+	 (equal (match-string 0) "=")
+	 (goto-char (match-end 0)))
+	(prog1
+	    (progn (c-forward-syntactic-ws limit)
+		   (point))
+	  (c-forward-constraint-clause limit stop-at-end))
+      (goto-char here)
+      nil)))
+
+(defun c-looking-at-concept (&optional limit)
+  ;; Are we currently at the start of a concept construct?  I.e. at the
+  ;; "template" keyword followed by the construct?  If so, we return a cons of
+  ;; the position of "concept" and the position of the first constraint
+  ;; expression following the "=" sign, otherwise we return nil.  LIMIT is a
+  ;; forward search limit.
+  (save-excursion
+    (let (conpos)
+      (and (looking-at c-pre-concept-<>-key)
+	   (goto-char (match-end 1))
+	   (< (point) limit)
+	   (progn (c-forward-syntactic-ws limit)
+		  (eq (char-after) ?<))
+	   (let ((c-parse-and-markup-<>-arglists t)
+		 c-restricted-<>-arglists)
+	     (c-forward-<>-arglist nil))
+	   (< (point) limit)
+	   (progn (c-forward-syntactic-ws limit)
+		  (looking-at c-equals-nontype-decl-key)) ; "concept"
+	   (setq conpos (match-beginning 0))
+	   (goto-char (match-end 0))
+	   (< (point) limit)
+	   (c-syntactic-re-search-forward
+	    "=" limit t t)
+	   (goto-char (match-end 0))
+	   (<= (point) limit)
+	   (progn (c-forward-syntactic-ws limit)
+		  (cons conpos (point)))))))
+
+(defun c-in-requires-or-at-end-of-clause (&optional pos)
+  ;; Is POS (default POINT) in a C++ "requires" expression or "requires"
+  ;; clause or at the end of a "requires" clause?  If so return a cons
+  ;; (POSITION . END) where POSITION is that of the "requires" keyword, and
+  ;; END is `expression' if POS is in an expression, nil if it's in a clause
+  ;; or t if it's at the end of a clause.  "End of a clause" means just after
+  ;; the non syntactic WS on the line where the clause ends.
+  ;;
+  ;; Note we can't use `c-beginning-of-statement-1' in this function because
+  ;; of this function's use in `c-at-vsemi-p' for C++ Mode.
+  (save-excursion
+    (if pos (goto-char pos) (setq pos (point)))
+    (let ((limit (max (- (point) 2000) (point-min)))
+	  found-req req-pos found-clause res pe-start pe-end
+	  )
+      (while	  ; Loop around syntactically significant "requires" keywords.
+	  (progn
+	    (while
+		(and
+		 (setq found-req (re-search-backward
+				  c-fun-name-substitute-key
+				  limit t)) ; Fast!
+		 (or (not (setq found-req
+				(not (eq (char-after (match-end 0)) ?_))))
+		     (not (setq found-req (not (c-in-literal))))))) ; Slow!
+	    (setq req-pos (point))
+	    (cond
+	     ((not found-req)		; No "requires" found
+	      nil)
+	     ((save-excursion		; A primary expression `pos' is in
+		(setq pe-end nil)
+		(while (and (setq pe-start (point))
+			    (< (point) pos)
+			    (c-forward-primary-expression nil t)
+			    (setq pe-end (point))
+			    (progn (c-forward-syntactic-ws)
+				   (looking-at "&&\\|||"))
+			    (c-forward-over-token-and-ws)))
+		pe-end)
+	      (if (<= pe-end pos)
+		  t 			; POS is not in a primary expression.
+		(setq res (cons pe-start 'expression))
+		nil))
+	     ((progn
+		(goto-char req-pos)
+		(if (looking-at c-fun-name-substitute-key)
+		    (setq found-clause (c-forward-c++-requires-clause nil t))
+		  (and (c-forward-concept-fragment)
+		       (setq found-clause (point))))
+		nil))
+	     ((and found-clause (>= (point) pos))
+	      (setq res (cons req-pos (eq (point) pos)))
+	      nil)
+	     (found-clause ; We found a constraint clause, but it did not
+	                   ; extend far enough forward to reach POS.
+	      (c-go-up-list-backward req-pos limit))
+	     (t (goto-char req-pos)
+		t))))
+      res)))
+
 (defun c-looking-at-inexpr-block (lim containing-sexp &optional check-at-end)
   ;; Return non-nil if we're looking at the beginning of a block
   ;; inside an expression.  The value returned is actually a cons of
@@ -13419,6 +13573,20 @@
 	 (looking-at c-pre-lambda-tokens-re)))
    (not (c-in-literal))))
 
+(defun c-c++-vsemi-p (&optional pos)
+  ;; C++ Only - Is there a "virtual semicolon" at POS or point?
+  ;; (See cc-defs.el for full details of "virtual semicolons".)
+  ;;
+  ;; This is true either when point is at the last non syntactic WS position
+  ;; on the line, and either there is a "macro with semicolon" just before it
+  ;; (see `c-at-macro-vsemi-p') or there is a "requires" clause which ends
+  ;; there.
+  (let (res)
+    (cond
+     ((setq res (c-in-requires-or-at-end-of-clause pos))
+      (and res (eq (cdr res) t)))
+     ((c-at-macro-vsemi-p)))))
+
 (defun c-at-macro-vsemi-p (&optional pos)
   ;; Is there a "virtual semicolon" at POS or point?
   ;; (See cc-defs.el for full details of "virtual semicolons".)
@@ -13970,7 +14138,7 @@
 	 literal char-before-ip before-ws-ip char-after-ip macro-start
 	 in-macro-expr c-syntactic-context placeholder
 	 step-type tmpsymbol keyword injava-inher special-brace-list tmp-pos
-	 tmp-pos2 containing-<
+	 tmp-pos2 containing-< tmp constraint-detail
 	 ;; The following record some positions for the containing
 	 ;; declaration block if we're directly within one:
 	 ;; `containing-decl-open' is the position of the open
@@ -14385,6 +14553,33 @@
 				containing-decl-start
 				containing-decl-kwd))
 
+	   ;; CASE 5A.7: "defun" open in a requires expression.
+	   ((save-excursion
+	      (goto-char indent-point)
+	      (c-backward-syntactic-ws lim)
+	      (and (or (not (eq (char-before) ?\)))
+		       (c-go-list-backward nil lim))
+		   (progn (c-backward-syntactic-ws lim)
+			  (zerop (c-backward-token-2 nil nil lim)))
+		   (looking-at c-fun-name-substitute-key)
+		   (not (eq (char-after (match-end 0)) ?_))
+		   (setq placeholder (point))))
+	    (goto-char placeholder)
+	    (back-to-indentation)
+	    (c-add-syntax 'defun-open (point)))
+
+	   ;; CASE 5A.6: "defun" open in concept.
+	   ;; ((save-excursion
+	   ;;    (goto-char indent-point)
+	   ;;    (skip-chars-forward " \t")
+	   ;;    (and (eq (char-after) ?{)
+	   ;; 	   (eq (c-beginning-of-statement-1 lim) 'same)
+	   ;; 	   (setq placeholder
+	   ;; 		 (cdr (c-looking-at-concept indent-point)))))
+	   ;;  (goto-char placeholder)
+	   ;;  (back-to-indentation)
+	   ;;  (c-add-syntax 'defun-open (point)))
+
 	   ;; CASE 5A.5: ordinary defun open
 	   (t
 	    (save-excursion
@@ -14555,10 +14750,35 @@
 	   nil nil
 	   containing-sexp paren-state))
 
-	 ;; CASE 5D: this could be a top-level initialization, a
+	 ;; CASE 5F: Close of a non-class declaration level block.
+	 ((and (eq char-after-ip ?})
+	       (c-keyword-member containing-decl-kwd
+				 'c-other-block-decl-kwds))
+	  ;; This is inconsistent: Should use `containing-decl-open'
+	  ;; here if it's at boi, like in case 5J.
+	  (goto-char containing-decl-start)
+	  (c-add-stmt-syntax
+	   (if (string-equal (symbol-name containing-decl-kwd) "extern")
+	       ;; Special case for compatibility with the
+	       ;; extern-lang syntactic symbols.
+	       'extern-lang-close
+	     (intern (concat (symbol-name containing-decl-kwd)
+			     "-close")))
+	   nil t
+	   (c-most-enclosing-brace paren-state (point))
+	   paren-state))
+
+	   ;; CASE 5T: Continuation of a concept clause.
+	 ((save-excursion
+	    (and (eq (c-beginning-of-statement-1 nil t) 'same)
+		 (setq tmp (c-looking-at-concept indent-point))))
+	  (c-add-syntax 'constraint-cont (car tmp)))
+
+	   ;; CASE 5D: this could be a top-level initialization, a
 	 ;; member init list continuation, or a template argument
 	 ;; list continuation.
 	 ((save-excursion
+	    (setq constraint-detail (c-in-requires-or-at-end-of-clause))
 	    ;; Note: We use the fact that lim is always after any
 	    ;; preceding brace sexp.
 	    (if c-recognize-<>-arglists
@@ -14588,8 +14808,9 @@
 	      ;; clause - we assume only C++ needs it.
 	      (c-syntactic-skip-backward "^;,=" lim t))
 	    (setq placeholder (point))
+	    (or constraint-detail
 	    (and (memq (char-before) '(?, ?= ?<))
-		 (not (c-crosses-statement-barrier-p (point) indent-point))))
+		 (not (c-crosses-statement-barrier-p (point) indent-point)))))
 	  (cond
 
 	   ;; CASE 5D.6: Something like C++11's "using foo = <type-exp>"
@@ -14658,21 +14879,10 @@
 
 	   ;; CASE 5D.7: Continuation of a "concept foo =" line in C++20 (or
 	   ;; similar).
-	   ((and c-equals-nontype-decl-key
-		 (save-excursion
-		   (prog1
-		       (and (zerop (c-backward-token-2 1 nil lim))
-			    (looking-at c-operator-re)
-			    (equal (match-string 0) "=")
-			    (zerop (c-backward-token-2 1 nil lim))
-			    (looking-at c-symbol-start)
-			    (not (looking-at c-keywords-regexp))
-			    (zerop (c-backward-token-2 1 nil lim))
-			    (looking-at c-equals-nontype-decl-key)
-			    (eq (c-beginning-of-statement-1 lim) 'same))
-		     (setq placeholder (point)))))
-	    (goto-char placeholder)
-	    (c-add-stmt-syntax 'topmost-intro-cont nil nil containing-sexp
+	   ((and constraint-detail
+		 (not (eq (cdr constraint-detail) 'expression)))
+	    (goto-char (car constraint-detail))
+	    (c-add-stmt-syntax 'constraint-cont nil nil containing-sexp
 			       paren-state))
 
 	   ;; CASE 5D.5: Continuation of the "expression part" of a
@@ -14697,24 +14907,6 @@
 	     nil nil containing-sexp paren-state))
 	   ))
 
-	 ;; CASE 5F: Close of a non-class declaration level block.
-	 ((and (eq char-after-ip ?})
-	       (c-keyword-member containing-decl-kwd
-				 'c-other-block-decl-kwds))
-	  ;; This is inconsistent: Should use `containing-decl-open'
-	  ;; here if it's at boi, like in case 5J.
-	  (goto-char containing-decl-start)
-	  (c-add-stmt-syntax
-	   (if (string-equal (symbol-name containing-decl-kwd) "extern")
-	       ;; Special case for compatibility with the
-	       ;; extern-lang syntactic symbols.
-	       'extern-lang-close
-	     (intern (concat (symbol-name containing-decl-kwd)
-			     "-close")))
-	   nil t
-	   (c-most-enclosing-brace paren-state (point))
-	   paren-state))
-
 	 ;; CASE 5G: we are looking at the brace which closes the
 	 ;; enclosing nested class decl
 	 ((and containing-sexp
@@ -14927,6 +15119,16 @@
 	  (c-add-syntax 'topmost-intro-cont (c-point 'boi)))
 	 ))
 
+       ;; CASE 20: A C++ requires sub-clause.
+       ((and (setq tmp (c-in-requires-or-at-end-of-clause indent-point))
+	     (not (eq (cdr tmp) 'expression))
+	     (setq placeholder (car tmp)))
+	(c-add-syntax
+	 (if (eq char-after-ip ?{)
+	     'substatement-open
+	   'substatement)
+	 (c-point 'boi placeholder)))
+
        ;; ((Old) CASE 6 has been removed.)
        ;; CASE 6: line is within a C11 _Generic expression.
        ((and c-generic-key
@@ -15310,6 +15512,20 @@
 	      (c-add-syntax 'defun-close (point))
 	    (c-add-syntax 'inline-close (point))))
 
+	 ;; CASE 16G: Do we have the closing brace of a "requires" clause
+	 ;; of a C++20 "concept"?
+	 ((save-excursion
+	    (c-backward-syntactic-ws lim)
+	    (and (or (not (eq (char-before) ?\)))
+		     (c-go-list-backward nil lim))
+		 (progn (c-backward-syntactic-ws lim)
+			(zerop (c-backward-token-2 nil nil lim)))
+		 (looking-at c-fun-name-substitute-key)
+		 (not (eq (char-after (match-end 0)) ?_))))
+	  (goto-char containing-sexp)
+	  (back-to-indentation)
+	  (c-add-stmt-syntax 'defun-close nil t lim paren-state))
+
 	 ;; CASE 16F: Can be a defun-close of a function declared
 	 ;; in a statement block, e.g. in Pike or when using gcc
 	 ;; extensions, but watch out for macros followed by
@@ -15460,6 +15676,21 @@
 	  (if (eq char-after-ip ?{)
 	      (c-add-syntax 'block-open)))
 
+	 ;; CASE 17J: first "statement" inside a C++20 requires
+	 ;; "function".
+	 ((save-excursion
+	    (goto-char containing-sexp)
+	    (c-backward-syntactic-ws lim)
+	    (and (or (not (eq (char-before) ?\)))
+		     (c-go-list-backward nil lim))
+		 (progn (c-backward-syntactic-ws lim)
+			(zerop (c-backward-token-2 nil nil lim)))
+		 (looking-at c-fun-name-substitute-key)
+		 (not (eq (char-after (match-end 0)) ?_))))
+	  (goto-char containing-sexp)
+	  (back-to-indentation)
+	  (c-add-syntax 'defun-block-intro (point)))
+
 	 ;; CASE 17F: first statement in an inline, or first
 	 ;; statement in a top-level defun. we can tell this is it
 	 ;; if there are no enclosing braces that haven't been
diff -r 300ebf19cd62 cc-fonts.el
--- a/cc-fonts.el	Fri Feb 17 08:58:11 2023 +0000
+++ b/cc-fonts.el	Mon Apr 03 19:49:17 2023 +0000
@@ -1389,7 +1389,8 @@
 				  (memq type '(c-decl-arg-start
 					       c-decl-type-start))))))))
 		      ((and (zerop (c-backward-token-2))
-			    (looking-at c-fun-name-substitute-key)))))))))
+			    (looking-at c-fun-name-substitute-key)
+			    (not (eq (char-after (match-end 0)) ?_))))))))))
       ;; Cache the result of this test for next time around.
       (c-put-char-property (1- match-pos) 'c-type 'c-decl-arg-start)
       (cons 'decl nil))
diff -r 300ebf19cd62 cc-langs.el
--- a/cc-langs.el	Fri Feb 17 08:58:11 2023 +0000
+++ b/cc-langs.el	Mon Apr 03 19:49:17 2023 +0000
@@ -571,7 +571,8 @@
 point), and returns nil or t.  This variable contains nil for languages which
 don't have EOL terminated statements. "
   t nil
-  (c c++ objc) 'c-at-macro-vsemi-p
+  (c objc) 'c-at-macro-vsemi-p
+  c++ 'c-c++-vsemi-p
   awk 'c-awk-at-vsemi-p)
 (c-lang-defvar c-at-vsemi-p-fn (c-lang-const c-at-vsemi-p-fn))
 
@@ -2620,9 +2621,12 @@
   c++ '("requires"))
 
 (c-lang-defconst c-fun-name-substitute-key
-  ;; An adorned regular expression which matches any member of
+  ;; An unadorned regular expression which matches any member of
   ;; `c-fun-name-substitute-kwds'.
-  t (c-make-keywords-re t (c-lang-const c-fun-name-substitute-kwds)))
+  t (c-make-keywords-re 'appendable (c-lang-const c-fun-name-substitute-kwds)))
+;; We use 'appendable, so that we get "\\>" on the regexp, but without a further
+;; character, which would mess up backward regexp search from just after the
+;; keyword.  If only XEmacs had \\_>.  ;-(
 (c-lang-defvar c-fun-name-substitute-key
 	       (c-lang-const c-fun-name-substitute-key))
 
diff -r 300ebf19cd62 cc-vars.el
--- a/cc-vars.el	Fri Feb 17 08:58:11 2023 +0000
+++ b/cc-vars.el	Mon Apr 03 19:49:17 2023 +0000
@@ -1095,6 +1095,8 @@
        ;; Anchor pos: Bol at the last line of previous construct.
        (topmost-intro-cont    . c-lineup-topmost-intro-cont)
        ;;Anchor pos: Bol at the topmost annotation line
+       (constraint-cont  . +)
+       ;; Anchor pos: Boi of the starting requires/concept line
        (annotation-top-cont   .   0)
        ;;Anchor pos: Bol at the topmost annotation line
        (annotation-var-cont   .   +)
@@ -1327,6 +1329,9 @@
  knr-argdecl		-- Subsequent lines in a K&R C argument declaration.
  topmost-intro		-- The first line in a topmost construct definition.
  topmost-intro-cont	-- Topmost definition continuation lines.
+ constraint-cont        -- Continuation line of a C++ requires clause (not
+                           to be confused with a \"requires expression\") or
+                           concept.
  annotation-top-cont    -- Topmost definition continuation line where only
  			   annotations are on previous lines.
  annotation-var-cont    -- A continuation of a C (or like) statement where


> -- 
> Michael Welsh Duggan
> (md5i <at> md5i.com)

-- 
Alan Mackenzie (Nuremberg, Germany).




Information forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Fri, 07 Apr 2023 16:01:01 GMT) Full text and rfc822 format available.

Message #26 received at 62386 <at> debbugs.gnu.org (full text, mbox):

From: Michael Welsh Duggan <mwd <at> md5i.com>
To: Alan Mackenzie <acm <at> muc.de>
Cc: Michael Welsh Duggan <mwd <at> md5i.com>, 62386 <at> debbugs.gnu.org
Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Fri, 07 Apr 2023 12:00:13 -0400
Alan Mackenzie <acm <at> muc.de> writes:

> Hello, Michael.
>
> On Tue, Mar 28, 2023 at 10:45:54 -0400, Michael Welsh Duggan wrote:
>> 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();
>
> I think I've now got these corrected.  The coding was much more difficult
> than I'd anticipated, with me spending several days in total confusion.
> The last thing which threw me off was the realisation that the
> expressions in the concept clause need not be "primary" expressions, i.e.
> something like !foo<T> is valid.  This is not the case with requires
> clauses, where only "primary" expressions are permitted.
>
>> 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.
>
> Thanks, you've just got to love the C++ Standards Committee.  ;-)
>
>> https://en.cppreference.com/w/cpp/language/constraints
>> https://en.cppreference.com/w/cpp/language/requires
>
> Thanks again, I've had those pages in my web browser cache for quite some
> time, now.
>
> Here's the latest version of the patch.  Please do the usual with it, and
> let me know how it goes.  Thanks!

Much, much better!  I have only one niggling nit left, and this one
doesn't bother me nearly as much as the others.  The nit is that opening
parentheses don't seem to open up a new indention context anymore.
(That's my internal mental terminology, not necessary what it used by
cc-mode.)

Before:

  template <typename T>
  requires (OctetLike<T>
            || (std::rank_v<T> == 1 && std::extent_v<T> == 0
                && OctetLike<std::remove_extent_t<T>>))
  Chunk(std::shared_ptr<T> const & p, length_t size)
  : data_{p, octet_cast(p.get())},
    size_{size}
  {}


After:

  template <typename T>
  requires (OctetLike<T>
    || (std::rank_v<T> == 1 && std::extent_v<T> == 0
    && OctetLike<std::remove_extent_t<T>>))
  Chunk(std::shared_ptr<T> const & p, length_t size)
    : data_{p, octet_cast(p.get())},
      size_{size}
  {}

-- 
Michael Welsh Duggan
(md5i <at> md5i.com)




Information forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Fri, 14 Apr 2023 11:15:01 GMT) Full text and rfc822 format available.

Message #29 received at 62386 <at> debbugs.gnu.org (full text, mbox):

From: Alan Mackenzie <acm <at> muc.de>
To: Michael Welsh Duggan <mwd <at> md5i.com>
Cc: 62386 <at> debbugs.gnu.org
Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Fri, 14 Apr 2023 11:14:39 +0000
Hello, Michael.

Sorry for the delay, it's been a bit of a rough week, here.

On Fri, Apr 07, 2023 at 12:00:13 -0400, Michael Welsh Duggan wrote:
> Alan Mackenzie <acm <at> muc.de> writes:

[ .... ]

> Much, much better!

Thanks for the testing.

> I have only one niggling nit left, and this one doesn't bother me
> nearly as much as the others.  The nit is that opening parentheses
> don't seem to open up a new indention context anymore.  (That's my
> internal mental terminology, not necessary what it used by cc-mode.)

> Before:

>   template <typename T>
>   requires (OctetLike<T>
>             || (std::rank_v<T> == 1 && std::extent_v<T> == 0
>                 && OctetLike<std::remove_extent_t<T>>))
>   Chunk(std::shared_ptr<T> const & p, length_t size)
>   : data_{p, octet_cast(p.get())},
>     size_{size}
>   {}


> After:

>   template <typename T>
>   requires (OctetLike<T>
>     || (std::rank_v<T> == 1 && std::extent_v<T> == 0
>     && OctetLike<std::remove_extent_t<T>>))
>   Chunk(std::shared_ptr<T> const & p, length_t size)
>     : data_{p, octet_cast(p.get())},
>       size_{size}
>   {}

This is a difficult one.  Aligning the || under OctetLike causes problems
when the parenthesis is already far to the right, for example if requires
is on the same line as template.  Then everything will be indented too
far to the right.  This dilemma has come up at least twice before.

I think what's worse is the lack of further indentation of the && line.
I don't know what to do about that, at the moment.

I'm also a little bit worried about the "virtual semicolon" detection on
some more complicated requires constructs.

Anyhow, I think I'll commit what I've got, and leave these doubts to be
cleared up later.

> -- 
> Michael Welsh Duggan
> (md5i <at> md5i.com)

-- 
Alan Mackenzie (Nuremberg, Germany).




Information forwarded to bug-cc-mode <at> gnu.org:
bug#62386; Package cc-mode. (Fri, 14 Apr 2023 12:04:02 GMT) Full text and rfc822 format available.

Message #32 received at 62386 <at> debbugs.gnu.org (full text, mbox):

From: Michael Welsh Duggan <mwd <at> md5i.com>
To: Alan Mackenzie <acm <at> muc.de>
Cc: Michael Welsh Duggan <mwd <at> md5i.com>, 62386 <at> debbugs.gnu.org
Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Fri, 14 Apr 2023 08:03:40 -0400
Alan Mackenzie <acm <at> muc.de> writes:

>> Before:
>
>>   template <typename T>
>>   requires (OctetLike<T>
>>             || (std::rank_v<T> == 1 && std::extent_v<T> == 0
>>                 && OctetLike<std::remove_extent_t<T>>))
>>   Chunk(std::shared_ptr<T> const & p, length_t size)
>>   : data_{p, octet_cast(p.get())},
>>     size_{size}
>>   {}
>
>
>> After:
>
>>   template <typename T>
>>   requires (OctetLike<T>
>>     || (std::rank_v<T> == 1 && std::extent_v<T> == 0
>>     && OctetLike<std::remove_extent_t<T>>))
>>   Chunk(std::shared_ptr<T> const & p, length_t size)
>>     : data_{p, octet_cast(p.get())},
>>       size_{size}
>>   {}
>
> This is a difficult one.  Aligning the || under OctetLike causes problems
> when the parenthesis is already far to the right, for example if requires
> is on the same line as template.  Then everything will be indented too
> far to the right.  This dilemma has come up at least twice before.
>
> I think what's worse is the lack of further indentation of the && line.
> I don't know what to do about that, at the moment.

I guess I just see it as the standard parenthesized expression problem
that already exists.  It's generally up to the programmer to make sure
opening parentheses don't happen too far to the right.

I agree about the && line.

> I'm also a little bit worried about the "virtual semicolon" detection on
> some more complicated requires constructs.
>
> Anyhow, I think I'll commit what I've got, and leave these doubts to be
> cleared up later.

Sure.  I'm generally happy with the improvements that have been made so
far.

-- 
Michael Welsh Duggan
(md5i <at> md5i.com)




Reply sent to Alan Mackenzie <acm <at> muc.de>:
You have taken responsibility. (Fri, 14 Apr 2023 17:02:02 GMT) Full text and rfc822 format available.

Notification sent to Michael Welsh Duggan <mwd <at> md5i.com>:
bug acknowledged by developer. (Fri, 14 Apr 2023 17:02:02 GMT) Full text and rfc822 format available.

Message #37 received at 62386-done <at> debbugs.gnu.org (full text, mbox):

From: Alan Mackenzie <acm <at> muc.de>
To: Michael Welsh Duggan <mwd <at> md5i.com>
Cc: acm <at> muc.de, 62386-done <at> debbugs.gnu.org
Subject: Re: bug#62386: CC Mode 5.35.2 (C++//l); C++ concept indentation
Date: Fri, 14 Apr 2023 17:01:13 +0000
Hello, Michael.

On Fri, Apr 14, 2023 at 08:03:40 -0400, Michael Welsh Duggan wrote:
> Alan Mackenzie <acm <at> muc.de> writes:

> > This is a difficult one.  Aligning the || under OctetLike causes problems
> > when the parenthesis is already far to the right, for example if requires
> > is on the same line as template.  Then everything will be indented too
> > far to the right.  This dilemma has come up at least twice before.

> > I think what's worse is the lack of further indentation of the && line.
> > I don't know what to do about that, at the moment.

> I guess I just see it as the standard parenthesized expression problem
> that already exists.  It's generally up to the programmer to make sure
> opening parentheses don't happen too far to the right.

> I agree about the && line.

> > I'm also a little bit worried about the "virtual semicolon" detection on
> > some more complicated requires constructs.

> > Anyhow, I think I'll commit what I've got, and leave these doubts to be
> > cleared up later.

> Sure.  I'm generally happy with the improvements that have been made so
> far.

OK, I've committed the patch, and I'm closing the bug, even though the
patch is imperfect.

> -- 
> Michael Welsh Duggan
> (md5i <at> md5i.com)

-- 
Alan Mackenzie (Nuremberg, Germany).




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Sat, 13 May 2023 11:24:09 GMT) Full text and rfc822 format available.

This bug report was last modified 2 years ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.