Commit Graph

6 Commits

Author SHA1 Message Date
William S Fulton 21adc1d7b6 cpp20_concepts: cover the remaining requires-expression body forms
Three additional concept / requires-expression tested.
All existing parser support (the requires-expression body
is skipped via skip_balanced):

- Inline 'requires requires' with a compound requirement (add_inline_same).
- A concept body listing multiple simple-requirements (Machine -> cycle).
- A concept body mixing the other three requirement kinds: a
  type-requirement, a noexcept compound-requirement and a nested
  requires-clause (BasicContainer -> check_container).
2026-05-10 17:37:52 +01:00
William S Fulton 1674f5ee64 C++20 concepts: support requires-expression compound-requirements
A requires-expression body may mix simple-requirements ('expr;')
with compound-requirements that carry a trailing return type
constraint ('{ expr } -> type-constraint;').
Add docs and an example testing this.

This works on existing parser support: the concept declaration is
silently consumed by skip_decl, whose skip_balanced('{', '}') already
handles the nested braces of the compound requirement, and the
trailing 'requires AddableSame<T>' goes through the existing
skip_constraint path

Also add test for 'requires requires' form with compound-requirement.
2026-05-10 17:37:51 +01:00
William S Fulton a8e62443c3 C++20 concepts: add Summable concept defined by a requires-expression
Show the named concept counterpart to the inline 'requires requires'
form already covered by the cpp20_concepts test case: a concept whose
body is a requires-expression, used as the constraint of a function
template.  Distinct name (Summable) so it does not clash with the
Addable variable template in cpp20_variable_templates.

  template<typename T>
  concept Summable = requires (T t) { t + t; };

  template<typename T>
  T sum_pair(T a, T b) requires Summable<T> {
    return a + b;
  }

This works on existing parser support: the concept declaration is
silently consumed by skip_decl, and the trailing 'requires Summable<T>'
goes through the existing skip_constraint path.
2026-05-10 17:37:51 +01:00
William S Fulton 8e6dd3948b C++20 concepts: support requires-expressions inside a constraint
A requires-expression of the form 'requires [(parms)] { body }' is one
of the primary forms a C++20 constraint can take, e.g.

  template<typename T>
  T add(T a, T b) requires requires (T t) { t + t; } {
    return a + b;
  }

The trailing form skip helper (skip_constraint in cscanner.c) was
walking tokens until it saw '{', ';' or '=' at depth 0 while tracking
only paren and bracket depth.  The inner '{ t + t; }' of the
requires-expression body therefore terminated the skip prematurely,
leaving the function body unparsed and producing a spurious syntax
error on the next token.

Teach skip_constraint to recognise a 'requires' identifier at depth 0
and treat it together with its optional '(parms)' and required
'{ body }' as one opaque primary - balancing the body via
Scanner_skip_balanced rather than letting its '{' end the constraint.

The cpp20_concepts test case grows an 'add' function template that
exercises this form, with matching Python and Java runme assertions
checking that add_int(2, 3) == 5 and add_int(-7, 4) == -3.  The C++20
manual chapter is updated to move the requires-expression-as-constraint-
primary form from the limitations list back to the supported set; the
'requires-expression bound to a namespace scope variable' form remains
unsupported.

Assisted-by: Claude Code (Opus 4.7)
2026-05-10 17:37:51 +01:00
William S Fulton 557842815c C++20 concepts: support prefix requires-clauses
A C++20 prefix requires-clause sits between the closing '>' of the
template parameter list and the start of the declaration it constrains:

  template<typename T> requires Numeric<T> && std::integral<T>
  T quad(T x) { return x * x * x * x; }

The previous commit handled the trailing form because the constraint is
followed by exactly one of three terminators ('{', ';' or '=') and a
token level skip helper just reads tokens, balancing '()' and '[]',
until it sees one of those at depth 0 and pushes it back.  The prefix
form has no such terminator: after 'requires Numeric<T> && std::integral<T>'
the next token is 'T' - the start of the return type of quad - but 'T'
can also legally appear inside a constraint (e.g. 'Numeric<T>'), so a
pure character level skip cannot tell whether 'T' belongs to the
constraint or to the declarator that follows it.

The C++20 grammar resolves this by defining a constraint as a
constraint-logical-or-expression:

  constraint-logical-or-expression:
      constraint-logical-and-expression
      constraint-logical-or-expression  ||  constraint-logical-and-expression

  constraint-logical-and-expression:
      primary-expression
      constraint-logical-and-expression  &&  primary-expression

After consuming one primary the next token decides everything: if it is
'&&' or '||' another primary follows; otherwise the constraint is over
and that token belongs to the declarator.  This commit implements that
loop directly at the scanner level (skip_constraint_primary plus
skip_prefix_requires_clause in cscanner.c) so the bison grammar does
not have to model the constraint expression at all.  A primary is a
parenthesised group, a requires-expression, or an id chain optionally
extended with '::' segments and a single template arg list, all of
which are bounded constructs that existing scanner helpers already
balance correctly.

The grammar change is a single new requires_clause_opt nonterminal
inserted between GREATERTHAN and the mid rule action of cpp_template_decl;
its REQUIRES alternative calls skip_prefix_requires_clause and
contributes nothing to the parse tree value.  Bison reports no new
shift/reduce conflicts under -Wall -Werror.

Test cases and Python/Java runme files are extended to cover prefix
requires-clauses with bare, '&&'-compounded, and parenthesised forms.
The manual chapter and CHANGES.current entry are updated accordingly.

Assisted-by: Claude Code (Opus 4.7)
2026-05-10 17:37:51 +01:00
William S Fulton bbcc01c2f3 Basic C++20 concept and trailing requires-clause support
Add minimal parser support so SWIG no longer rejects C++20 concept
declarations or function templates with a trailing requires-clause.
Both constructs are silently consumed and produce no parse tree node,
so a constrained function template wraps as if it were unconstrained.

Implementation: new CONCEPT and REQUIRES tokens are recognised in the
C++ scanner alongside final/override.  cpp_concept_decl is added to
cpp_template_possible and uses the existing skip_decl() helper to eat
the whole declaration.  cpp_const gains two alternatives that consume
a trailing requires-clause via a new skip_constraint() helper, which
reads tokens until top level '{', ';' or '=' while balancing () and [].

Prefix requires-clauses (between the template head and the declarator)
are not yet supported - the constraint terminator in that position
isn't reliably detectable at lex level.

Adds the cpp20_concepts test case (Numeric and SmallNumeric concepts,
a cube function template constrained by a trailing requires-clause,
%template instantiations for int and double), Python and Java runme
files exercising those instantiations, and a Concepts and
requires-clauses section in the C++20 manual chapter (Contents.html
regenerated via make maketoc).

Assisted-by: Claude Code (Opus 4.7)
2026-05-10 17:37:50 +01:00