C++, fix parsing of certain non-type template parameters

Specifically 'template<int (X::*)(bool)...>'
This commit is contained in:
Jakob Lykke Andersen 2022-07-29 17:49:01 +02:00
parent a95949aa13
commit aa43a37865
3 changed files with 24 additions and 10 deletions

View File

@ -21,6 +21,7 @@ Bugs fixed
- #10257: C++, ensure consistent non-specialization template argument - #10257: C++, ensure consistent non-specialization template argument
representation. representation.
- #10729: C++, fix parsing of certain non-type template parameter packs.
Testing Testing
-------- --------

View File

@ -3668,9 +3668,11 @@ class ASTTemplateParamTemplateType(ASTTemplateParam):
class ASTTemplateParamNonType(ASTTemplateParam): class ASTTemplateParamNonType(ASTTemplateParam):
def __init__(self, def __init__(self,
param: Union[ASTTypeWithInit, param: Union[ASTTypeWithInit,
ASTTemplateParamConstrainedTypeWithInit]) -> None: ASTTemplateParamConstrainedTypeWithInit],
parameterPack: bool = False) -> None:
assert param assert param
self.param = param self.param = param
self.parameterPack = parameterPack
@property @property
def name(self) -> ASTNestedName: def name(self) -> ASTNestedName:
@ -3679,7 +3681,7 @@ class ASTTemplateParamNonType(ASTTemplateParam):
@property @property
def isPack(self) -> bool: def isPack(self) -> bool:
return self.param.isPack return self.param.isPack or self.parameterPack
def get_identifier(self) -> ASTIdentifier: def get_identifier(self) -> ASTIdentifier:
name = self.param.name name = self.param.name
@ -3700,14 +3702,22 @@ class ASTTemplateParamNonType(ASTTemplateParam):
# the anchor will be our parent # the anchor will be our parent
return symbol.parent.declaration.get_id(version, prefixed=None) return symbol.parent.declaration.get_id(version, prefixed=None)
else: else:
return '_' + self.param.get_id(version) res = '_'
if self.parameterPack:
res += 'Dp'
return res + self.param.get_id(version)
def _stringify(self, transform: StringifyTransform) -> str: def _stringify(self, transform: StringifyTransform) -> str:
return transform(self.param) res = transform(self.param)
if self.parameterPack:
res += '...'
return res
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
self.param.describe_signature(signode, mode, env, symbol) self.param.describe_signature(signode, mode, env, symbol)
if self.parameterPack:
signode += addnodes.desc_sig_punctuation('...', '...')
class ASTTemplateParams(ASTBase): class ASTTemplateParams(ASTBase):
@ -4155,7 +4165,7 @@ class LookupKey:
def _is_specialization(templateParams: Union[ASTTemplateParams, ASTTemplateIntroduction], def _is_specialization(templateParams: Union[ASTTemplateParams, ASTTemplateIntroduction],
templateArgs: ASTTemplateArgs) -> bool: templateArgs: ASTTemplateArgs) -> bool:
"""Checks if `templateArgs` does not exactly match `templateParams`.""" # Checks if `templateArgs` does not exactly match `templateParams`.
# the names of the template parameters must be given exactly as args # the names of the template parameters must be given exactly as args
# and params that are packs must in the args be the name expanded # and params that are packs must in the args be the name expanded
if len(templateParams.params) != len(templateArgs.args): if len(templateParams.params) != len(templateArgs.args):
@ -6775,7 +6785,7 @@ class DefinitionParser(BaseParser):
def _parse_template_parameter(self) -> ASTTemplateParam: def _parse_template_parameter(self) -> ASTTemplateParam:
self.skip_ws() self.skip_ws()
if self.skip_word('template'): if self.skip_word('template'):
# declare a tenplate template parameter # declare a template template parameter
nestedParams = self._parse_template_parameter_list() nestedParams = self._parse_template_parameter_list()
else: else:
nestedParams = None nestedParams = None
@ -6822,7 +6832,9 @@ class DefinitionParser(BaseParser):
# non-type parameter or constrained type parameter # non-type parameter or constrained type parameter
self.pos = pos self.pos = pos
param = self._parse_type_with_init('maybe', 'templateParam') param = self._parse_type_with_init('maybe', 'templateParam')
return ASTTemplateParamNonType(param) self.skip_ws()
parameterPack = self.skip_string('...')
return ASTTemplateParamNonType(param, parameterPack)
except DefinitionError as eNonType: except DefinitionError as eNonType:
self.pos = pos self.pos = pos
header = "Error when parsing template parameter." header = "Error when parsing template parameter."

View File

@ -876,6 +876,9 @@ def test_domain_cpp_ast_templates():
# defaulted constrained type parameters # defaulted constrained type parameters
check('type', 'template<C T = int&> {key}A', {2: 'I_1CE1A'}, key='using') check('type', 'template<C T = int&> {key}A', {2: 'I_1CE1A'}, key='using')
# pack expansion after non-type template parameter
check('type', 'template<int (X::*)(bool)...> {key}A', {2: 'I_DpM1XFibEE1A'}, key='using')
def test_domain_cpp_ast_placeholder_types(): def test_domain_cpp_ast_placeholder_types():
check('function', 'void f(Sortable auto &v)', {1: 'f__SortableR', 2: '1fR8Sortable'}) check('function', 'void f(Sortable auto &v)', {1: 'f__SortableR', 2: '1fR8Sortable'})
@ -1472,9 +1475,7 @@ def parse_template_parameter(param: str):
('int X::*...', True), ('int X::*...', True),
('int (X::*)(bool)', False), ('int (X::*)(bool)', False),
('int (X::*x)(bool)', False), ('int (X::*x)(bool)', False),
# TODO: the following two declarations cannot currently be parsed ('int (X::*)(bool)...', True),
# ('int (X::*)(bool)...', True),
# ('int (X::*x...)(bool)', True),
('template<typename> class', False), ('template<typename> class', False),
('template<typename> class...', True), ('template<typename> class...', True),
]) ])