Move non-template requires to the function

- Methods can have a requires() that refer to the class template without
  an explicit function template
- This is a breaking change, but since the values aren't parsed yet I
  can't imagine anyone is using it
This commit is contained in:
Dustin Spicuzza 2023-12-02 04:49:19 -05:00
parent f1708bf9b8
commit 85f93ec09e
3 changed files with 108 additions and 44 deletions

View File

@ -1907,12 +1907,10 @@ class CxxParser:
else: else:
rtok = self.lex.token_if("requires") rtok = self.lex.token_if("requires")
if rtok: if rtok:
fn_template = fn.template # requires on a function must always be accompanied by a template
if fn_template is None: if fn.template is None:
raise self._parse_error(rtok) raise self._parse_error(rtok)
elif isinstance(fn_template, list): fn.raw_requires = self._parse_requires(rtok)
fn_template = fn_template[0]
fn_template.raw_requires_post = self._parse_requires(rtok)
if self.lex.token_if("ARROW"): if self.lex.token_if("ARROW"):
self._parse_trailing_return_type(fn) self._parse_trailing_return_type(fn)
@ -1978,12 +1976,7 @@ class CxxParser:
toks = self._consume_balanced_tokens(otok)[1:-1] toks = self._consume_balanced_tokens(otok)[1:-1]
method.noexcept = self._create_value(toks) method.noexcept = self._create_value(toks)
elif tok_value == "requires": elif tok_value == "requires":
method_template = method.template method.raw_requires = self._parse_requires(tok)
if method_template is None:
raise self._parse_error(tok)
elif isinstance(method_template, list):
method_template = method_template[0]
method_template.raw_requires_post = self._parse_requires(tok)
else: else:
self.lex.return_token(tok) self.lex.return_token(tok)
break break

View File

@ -526,9 +526,6 @@ class TemplateDecl:
#: template <typename T> requires ... #: template <typename T> requires ...
raw_requires_pre: typing.Optional[Value] = None raw_requires_pre: typing.Optional[Value] = None
#: template <typename T> int main() requires ...
raw_requires_post: typing.Optional[Value] = None
#: If no template, this is None. This is a TemplateDecl if this there is a single #: If no template, this is None. This is a TemplateDecl if this there is a single
#: declaration: #: declaration:
@ -730,6 +727,13 @@ class Function:
#: is the string "conversion" and the full Type is found in return_type #: is the string "conversion" and the full Type is found in return_type
operator: typing.Optional[str] = None operator: typing.Optional[str] = None
#: A requires constraint following the function declaration. If you need the
#: prior, look at TemplateDecl.raw_requires_pre. At the moment this is just
#: a raw value, if we interpret it in the future this will change.
#:
#: template <typename T> int main() requires ...
raw_requires: typing.Optional[Value] = None
@dataclass @dataclass
class Method(Function): class Method(Function):

View File

@ -6,6 +6,7 @@ from cxxheaderparser.types import (
Concept, Concept,
Function, Function,
FundamentalSpecifier, FundamentalSpecifier,
Method,
MoveReference, MoveReference,
NameSpecifier, NameSpecifier,
PQName, PQName,
@ -495,8 +496,9 @@ def test_requires_last_elem() -> None:
) )
], ],
template=TemplateDecl( template=TemplateDecl(
params=[TemplateTypeParam(typekey="typename", name="T")], params=[TemplateTypeParam(typekey="typename", name="T")]
raw_requires_post=Value( ),
raw_requires=Value(
tokens=[ tokens=[
Token(value="Eq"), Token(value="Eq"),
Token(value="<"), Token(value="<"),
@ -504,7 +506,6 @@ def test_requires_last_elem() -> None:
Token(value=">"), Token(value=">"),
] ]
), ),
),
) )
] ]
) )
@ -752,7 +753,8 @@ def test_requires_both() -> None:
Token(value=">"), Token(value=">"),
] ]
), ),
raw_requires_post=Value( ),
raw_requires=Value(
tokens=[ tokens=[
Token(value="Subtractable"), Token(value="Subtractable"),
Token(value="<"), Token(value="<"),
@ -760,7 +762,6 @@ def test_requires_both() -> None:
Token(value=">"), Token(value=">"),
] ]
), ),
),
) )
] ]
) )
@ -791,8 +792,9 @@ def test_requires_paren() -> None:
) )
], ],
template=TemplateDecl( template=TemplateDecl(
params=[TemplateTypeParam(typekey="class", name="T")], params=[TemplateTypeParam(typekey="class", name="T")]
raw_requires_post=Value( ),
raw_requires=Value(
tokens=[ tokens=[
Token(value="("), Token(value="("),
Token(value="is_purrable"), Token(value="is_purrable"),
@ -804,7 +806,72 @@ def test_requires_paren() -> None:
Token(value=")"), Token(value=")"),
] ]
), ),
), )
]
)
)
def test_non_template_requires() -> None:
content = """
// clang-format off
template <class T>
struct Payload
{
constexpr Payload(T v)
requires(std::is_pod_v<T>)
: Value(v)
{
}
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
classes=[
ClassScope(
class_decl=ClassDecl(
typename=PQName(
segments=[NameSpecifier(name="Payload")], classkey="struct"
),
template=TemplateDecl(
params=[TemplateTypeParam(typekey="class", name="T")]
),
),
methods=[
Method(
return_type=None,
name=PQName(segments=[NameSpecifier(name="Payload")]),
parameters=[
Parameter(
type=Type(
typename=PQName(
segments=[NameSpecifier(name="T")]
)
),
name="v",
)
],
constexpr=True,
has_body=True,
raw_requires=Value(
tokens=[
Token(value="("),
Token(value="std"),
Token(value="::"),
Token(value="is_pod_v"),
Token(value="<"),
Token(value="T"),
Token(value=">"),
Token(value=")"),
]
),
access="public",
constructor=True,
)
],
) )
] ]
) )