Skip to content

Commit 85f93ec

Browse files
committed
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
1 parent f1708bf commit 85f93ec

File tree

3 files changed

+107
-43
lines changed

3 files changed

+107
-43
lines changed

cxxheaderparser/parser.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,12 +1907,10 @@ def _parse_fn_end(self, fn: Function) -> None:
19071907
else:
19081908
rtok = self.lex.token_if("requires")
19091909
if rtok:
1910-
fn_template = fn.template
1911-
if fn_template is None:
1910+
# requires on a function must always be accompanied by a template
1911+
if fn.template is None:
19121912
raise self._parse_error(rtok)
1913-
elif isinstance(fn_template, list):
1914-
fn_template = fn_template[0]
1915-
fn_template.raw_requires_post = self._parse_requires(rtok)
1913+
fn.raw_requires = self._parse_requires(rtok)
19161914

19171915
if self.lex.token_if("ARROW"):
19181916
self._parse_trailing_return_type(fn)
@@ -1978,12 +1976,7 @@ def _parse_method_end(self, method: Method) -> None:
19781976
toks = self._consume_balanced_tokens(otok)[1:-1]
19791977
method.noexcept = self._create_value(toks)
19801978
elif tok_value == "requires":
1981-
method_template = method.template
1982-
if method_template is None:
1983-
raise self._parse_error(tok)
1984-
elif isinstance(method_template, list):
1985-
method_template = method_template[0]
1986-
method_template.raw_requires_post = self._parse_requires(tok)
1979+
method.raw_requires = self._parse_requires(tok)
19871980
else:
19881981
self.lex.return_token(tok)
19891982
break

cxxheaderparser/types.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,9 +526,6 @@ class Foo {};
526526
#: template <typename T> requires ...
527527
raw_requires_pre: typing.Optional[Value] = None
528528

529-
#: template <typename T> int main() requires ...
530-
raw_requires_post: typing.Optional[Value] = None
531-
532529

533530
#: If no template, this is None. This is a TemplateDecl if this there is a single
534531
#: declaration:
@@ -730,6 +727,13 @@ class Function:
730727
#: is the string "conversion" and the full Type is found in return_type
731728
operator: typing.Optional[str] = None
732729

730+
#: A requires constraint following the function declaration. If you need the
731+
#: prior, look at TemplateDecl.raw_requires_pre. At the moment this is just
732+
#: a raw value, if we interpret it in the future this will change.
733+
#:
734+
#: template <typename T> int main() requires ...
735+
raw_requires: typing.Optional[Value] = None
736+
733737

734738
@dataclass
735739
class Method(Function):

tests/test_concepts.py

Lines changed: 96 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
Concept,
77
Function,
88
FundamentalSpecifier,
9+
Method,
910
MoveReference,
1011
NameSpecifier,
1112
PQName,
@@ -495,15 +496,15 @@ def test_requires_last_elem() -> None:
495496
)
496497
],
497498
template=TemplateDecl(
498-
params=[TemplateTypeParam(typekey="typename", name="T")],
499-
raw_requires_post=Value(
500-
tokens=[
501-
Token(value="Eq"),
502-
Token(value="<"),
503-
Token(value="T"),
504-
Token(value=">"),
505-
]
506-
),
499+
params=[TemplateTypeParam(typekey="typename", name="T")]
500+
),
501+
raw_requires=Value(
502+
tokens=[
503+
Token(value="Eq"),
504+
Token(value="<"),
505+
Token(value="T"),
506+
Token(value=">"),
507+
]
507508
),
508509
)
509510
]
@@ -752,14 +753,14 @@ def test_requires_both() -> None:
752753
Token(value=">"),
753754
]
754755
),
755-
raw_requires_post=Value(
756-
tokens=[
757-
Token(value="Subtractable"),
758-
Token(value="<"),
759-
Token(value="T"),
760-
Token(value=">"),
761-
]
762-
),
756+
),
757+
raw_requires=Value(
758+
tokens=[
759+
Token(value="Subtractable"),
760+
Token(value="<"),
761+
Token(value="T"),
762+
Token(value=">"),
763+
]
763764
),
764765
)
765766
]
@@ -791,20 +792,86 @@ def test_requires_paren() -> None:
791792
)
792793
],
793794
template=TemplateDecl(
794-
params=[TemplateTypeParam(typekey="class", name="T")],
795-
raw_requires_post=Value(
796-
tokens=[
797-
Token(value="("),
798-
Token(value="is_purrable"),
799-
Token(value="<"),
800-
Token(value="T"),
801-
Token(value=">"),
802-
Token(value="("),
803-
Token(value=")"),
804-
Token(value=")"),
805-
]
795+
params=[TemplateTypeParam(typekey="class", name="T")]
796+
),
797+
raw_requires=Value(
798+
tokens=[
799+
Token(value="("),
800+
Token(value="is_purrable"),
801+
Token(value="<"),
802+
Token(value="T"),
803+
Token(value=">"),
804+
Token(value="("),
805+
Token(value=")"),
806+
Token(value=")"),
807+
]
808+
),
809+
)
810+
]
811+
)
812+
)
813+
814+
815+
def test_non_template_requires() -> None:
816+
content = """
817+
// clang-format off
818+
819+
template <class T>
820+
struct Payload
821+
{
822+
constexpr Payload(T v)
823+
requires(std::is_pod_v<T>)
824+
: Value(v)
825+
{
826+
}
827+
};
828+
"""
829+
data = parse_string(content, cleandoc=True)
830+
831+
assert data == ParsedData(
832+
namespace=NamespaceScope(
833+
classes=[
834+
ClassScope(
835+
class_decl=ClassDecl(
836+
typename=PQName(
837+
segments=[NameSpecifier(name="Payload")], classkey="struct"
838+
),
839+
template=TemplateDecl(
840+
params=[TemplateTypeParam(typekey="class", name="T")]
806841
),
807842
),
843+
methods=[
844+
Method(
845+
return_type=None,
846+
name=PQName(segments=[NameSpecifier(name="Payload")]),
847+
parameters=[
848+
Parameter(
849+
type=Type(
850+
typename=PQName(
851+
segments=[NameSpecifier(name="T")]
852+
)
853+
),
854+
name="v",
855+
)
856+
],
857+
constexpr=True,
858+
has_body=True,
859+
raw_requires=Value(
860+
tokens=[
861+
Token(value="("),
862+
Token(value="std"),
863+
Token(value="::"),
864+
Token(value="is_pod_v"),
865+
Token(value="<"),
866+
Token(value="T"),
867+
Token(value=">"),
868+
Token(value=")"),
869+
]
870+
),
871+
access="public",
872+
constructor=True,
873+
)
874+
],
808875
)
809876
]
810877
)

0 commit comments

Comments
 (0)