LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 32166 - [feature request] requires clause in functions (concepts TS)
Summary: [feature request] requires clause in functions (concepts TS)
Status: CONFIRMED
Alias: None
Product: clang
Classification: Unclassified
Component: Formatter (show other bugs)
Version: trunk
Hardware: All All
: P enhancement
Assignee: Björn Schäpers
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-03-07 03:02 PST by Gonzalo BG
Modified: 2021-11-09 13:04 PST (History)
6 users (show)

See Also:
Fixed By Commit(s):


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Gonzalo BG 2017-03-07 03:02:14 PST
This code

template <bool IsConst>
struct A {
  void prev() 
  requires BidirectionalIterator<iterator_t<IsConst>>() &&
           BidirectionalIterator<iterator_t<IsConst>>() &&
           BidirectionalIterator<iterator_t<IsConst>>() 
  { --it_; }
};

gets formatted into: 

template <bool IsConst>
struct A {
  void prev() requires BidirectionalIterator<iterator_t<IsConst>>() &&
      BidirectionalIterator<iterator_t<IsConst>>() &&
      BidirectionalIterator<iterator_t<IsConst>>() {
    --it_;
  }
};

I don't see a reason why the options specified in #32165 shouldn't apply here as well. 

Also: https://github.com/CaseyCarter/cmcstl2/blob/43b63f6846d80bcd1867f512519bc18841bd961e/include/stl2/detail/iterator/operations.hpp#L25

this code: 

template <class I>
requires
        Iterator<I>()
        // Pre: 0 <= n && [i,i+n)
constexpr void impl(I& i, difference_type_t<I> n)
noexcept(noexcept(++std::declval<I&>()))
{
    STL2_EXPECT(0 <= n);
    while (n != 0) {
        --n;
        ++i;
    }
}

gets formatted into 

template <class I>
requires Iterator<I>()
    // Pre: 0 <= n && [i,i+n)
    constexpr void impl(I& i, difference_type_t<I> n) noexcept(
        noexcept(++std::declval<I&>())) {
  STL2_EXPECT(0 <= n);
  while (n != 0) {
    --n;
    ++i;
  }
}

(Note how the constexpr keywords is not aligned with the template keyword).
Comment 1 Casey Carter 2020-03-02 12:48:55 PST
Ditto Poke, poke. Now that C++20 has been sent out for (what will likely be) its final round of balloting, it would be a good time to work on supporting requires-clauses in clang-format. Think of all the poor, suffering STL maintainers!
Comment 2 MyDeveloperDay 2020-05-11 03:35:12 PDT
I've started to look into the concepts and requires keywords and how clang-format interacts with them (its doesn't really)

I'd like @Casey to start with a slightly simpler example (if we could start without the comment that might help)

For the following would you expect it to be formatted like this?

template <class I, class S>
requires Sentinel<S, I>() 
constexpr void advance(I &i, S bound) noexcept( noexcept(++i != bound)) {
  while (i != bound) {
    ++i;
  }
}


or something else? I'm trying to look for something of a common starting place between what I see in both

https://github.com/CaseyCarter/cmcstl2
and
https://github.com/microsoft/STL
Comment 3 Casey Carter 2020-05-11 19:01:01 PDT
(In reply to MyDeveloperDay from comment #2)
> I've started to look into the concepts and requires keywords and how
> clang-format interacts with them (its doesn't really)
> 
> I'd like @Casey to start with a slightly simpler example (if we could start
> without the comment that might help)
> 
> For the following would you expect it to be formatted like this?
> 
> template <class I, class S>
> requires Sentinel<S, I>() 
> constexpr void advance(I &i, S bound) noexcept( noexcept(++i != bound)) {
>   while (i != bound) {
>     ++i;
>   }
> }

Today, I'd format this as:

  template <class I, class S>
    requires Sentinel<S, I>()
  constexpr void advance(I& i, S bound) noexcept(noexcept(++i != bound)) {
    while (i != bound) {
      ++i;
    }
  }

with the requires-clause indented once relative to the template declaration, and continuation lines one indent deeper:

  template <class I, class S>
    requires Sentinel<S, I>() && // ...
      && OtherConcept1<I> && // ...
      && OtherConcept2<I> && // ...
      && OtherConcept3<I> && // ...
  constexpr void advance(I& i, S bound) noexcept(noexcept(++i != bound)) {
    while (i != bound) {
      ++i;
    }
  }

which probably follows from how clang normally wraps expressions across lines. I try to format trailing requires-clauses exactly as you've put the noexcept-specifier in the example, but wrapping the whole thing onto the next line if it won't fit on the primary declaration line and only splitting if the requires-clause itself is longer than one line:

  template <class I>
  void f(I i) requires Concept1<I> && Concept2<i> {
    // ...
  }

  template <class I>
  long_return_type g(I i) 
    requires Concept1<I> && Concept2<i> && VeryVeryVeryVeryLongConcept<I> {
    // ...
  }

  template <class I>
  long_return_type h(I i) requires Concept1<I> && Concept2<i> && 
    VeryVeryVeryVeryLongConcept<I> && OtherLongConcept<I> {
    // ...
  }

> or something else? I'm trying to look for something of a common starting
> place between what I see in both
> 
> https://github.com/CaseyCarter/cmcstl2
> and
> https://github.com/microsoft/STL

Please ignore cmcstl2: there a few different styles there that we experimented with over the years, and it was never really made consistent. range-v3 is even worse: its style represents a set of compromises to get the least offensive output from clang-format. I have no idea how libstdc++ is formatting their concepts usage, but they have C++20 Ranges implemented so they may have an interesting counterpoint to my style.
Comment 4 Björn Schäpers 2021-11-09 13:04:27 PST
I'm working on it.