Merge pull request #9379 from jakobandersen/cpp_various

C++ (and C), various additions
This commit is contained in:
Jakob Lykke Andersen 2021-06-24 23:14:51 +02:00 committed by GitHub
commit eda4b1cfc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 185 additions and 58 deletions

12
CHANGES
View File

@ -53,6 +53,18 @@ Features added
* #9097: Optimize the paralell build
* #9131: Add :confval:`nitpick_ignore_regex` to ignore nitpicky warnings using
regular expressions
* C++, add support for
- ``inline`` variables,
- ``consteval`` functions,
- ``constinit`` variables,
- ``char8_t``,
- ``explicit(<constant expression>)`` specifier,
- digit separators in literals, and
- constraints in placeholder type specifiers, aka. adjective syntax
(e.g., ``Sortable auto &v``).
* C, add support for digit separators in literals.
Bugs fixed

View File

@ -319,8 +319,9 @@ _fold_operator_re = re.compile(r'''(?x)
# see https://en.cppreference.com/w/cpp/keyword
_keywords = [
'alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor',
'bool', 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class',
'compl', 'concept', 'const', 'constexpr', 'const_cast', 'continue',
'bool', 'break', 'case', 'catch', 'char', 'char8_t', 'char16_t', 'char32_t',
'class', 'compl', 'concept', 'const', 'consteval', 'constexpr', 'constinit',
'const_cast', 'continue',
'decltype', 'default', 'delete', 'do', 'double', 'dynamic_cast', 'else',
'enum', 'explicit', 'export', 'extern', 'false', 'float', 'for', 'friend',
'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new',
@ -426,6 +427,7 @@ _id_fundamental_v2 = {
'wchar_t': 'w',
'char32_t': 'Di',
'char16_t': 'Ds',
'char8_t': 'Du',
'short': 's',
'short int': 's',
'signed short': 's',
@ -1880,9 +1882,11 @@ class ASTTrailingTypeSpecDecltype(ASTTrailingTypeSpec):
class ASTTrailingTypeSpecName(ASTTrailingTypeSpec):
def __init__(self, prefix: str, nestedName: ASTNestedName) -> None:
def __init__(self, prefix: str, nestedName: ASTNestedName,
placeholderType: Optional[str]) -> None:
self.prefix = prefix
self.nestedName = nestedName
self.placeholderType = placeholderType
@property
def name(self) -> ASTNestedName:
@ -1897,6 +1901,9 @@ class ASTTrailingTypeSpecName(ASTTrailingTypeSpec):
res.append(self.prefix)
res.append(' ')
res.append(transform(self.nestedName))
if self.placeholderType is not None:
res.append(' ')
res.append(self.placeholderType)
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
@ -1905,6 +1912,17 @@ class ASTTrailingTypeSpecName(ASTTrailingTypeSpec):
signode += addnodes.desc_sig_keyword(self.prefix, self.prefix)
signode += addnodes.desc_sig_space()
self.nestedName.describe_signature(signode, mode, env, symbol=symbol)
if self.placeholderType is not None:
signode += addnodes.desc_sig_space()
if self.placeholderType == 'auto':
signode += addnodes.desc_sig_keyword('auto', 'auto')
elif self.placeholderType == 'decltype(auto)':
signode += addnodes.desc_sig_keyword('decltype', 'decltype')
signode += addnodes.desc_sig_punctuation('(', '(')
signode += addnodes.desc_sig_keyword('auto', 'auto')
signode += addnodes.desc_sig_punctuation(')', ')')
else:
assert False, self.placeholderType
class ASTFunctionParameter(ASTBase):
@ -2099,16 +2117,41 @@ class ASTParametersQualifiers(ASTBase):
signode += addnodes.desc_sig_keyword(self.initializer, self.initializer)
class ASTExplicitSpec(ASTBase):
def __init__(self, expr: Optional[ASTExpression]) -> None:
self.expr = expr
def _stringify(self, transform: StringifyTransform) -> str:
res = ['explicit']
if self.expr is not None:
res.append('(')
res.append(transform(self.expr))
res.append(')')
return ''.join(res)
def describe_signature(self, signode: TextElement,
env: "BuildEnvironment", symbol: "Symbol") -> None:
signode += addnodes.desc_sig_keyword('explicit', 'explicit')
if self.expr is not None:
signode += addnodes.desc_sig_punctuation('(', '(')
self.expr.describe_signature(signode, 'markType', env, symbol)
signode += addnodes.desc_sig_punctuation(')', ')')
class ASTDeclSpecsSimple(ASTBase):
def __init__(self, storage: str, threadLocal: bool, inline: bool, virtual: bool,
explicit: bool, constexpr: bool, volatile: bool, const: bool,
friend: bool, attrs: List[ASTAttribute]) -> None:
explicitSpec: Optional[ASTExplicitSpec],
consteval: bool, constexpr: bool, constinit: bool,
volatile: bool, const: bool, friend: bool,
attrs: List[ASTAttribute]) -> None:
self.storage = storage
self.threadLocal = threadLocal
self.inline = inline
self.virtual = virtual
self.explicit = explicit
self.explicitSpec = explicitSpec
self.consteval = consteval
self.constexpr = constexpr
self.constinit = constinit
self.volatile = volatile
self.const = const
self.friend = friend
@ -2121,8 +2164,10 @@ class ASTDeclSpecsSimple(ASTBase):
self.threadLocal or other.threadLocal,
self.inline or other.inline,
self.virtual or other.virtual,
self.explicit or other.explicit,
self.explicitSpec or other.explicitSpec,
self.consteval or other.consteval,
self.constexpr or other.constexpr,
self.constinit or other.constinit,
self.volatile or other.volatile,
self.const or other.const,
self.friend or other.friend,
@ -2141,17 +2186,22 @@ class ASTDeclSpecsSimple(ASTBase):
res.append('friend')
if self.virtual:
res.append('virtual')
if self.explicit:
res.append('explicit')
if self.explicitSpec:
res.append(transform(self.explicitSpec))
if self.consteval:
res.append('consteval')
if self.constexpr:
res.append('constexpr')
if self.constinit:
res.append('constinit')
if self.volatile:
res.append('volatile')
if self.const:
res.append('const')
return ' '.join(res)
def describe_signature(self, signode: TextElement) -> None:
def describe_signature(self, signode: TextElement,
env: "BuildEnvironment", symbol: "Symbol") -> None:
addSpace = False
for attr in self.attrs:
if addSpace:
@ -2175,10 +2225,17 @@ class ASTDeclSpecsSimple(ASTBase):
addSpace = _add(signode, 'friend')
if self.virtual:
addSpace = _add(signode, 'virtual')
if self.explicit:
addSpace = _add(signode, 'explicit')
if self.explicitSpec:
if addSpace:
signode += addnodes.desc_sig_space()
self.explicitSpec.describe_signature(signode, env, symbol)
addSpace = True
if self.consteval:
addSpace = _add(signode, 'consteval')
if self.constexpr:
addSpace = _add(signode, 'constexpr')
if self.constinit:
addSpace = _add(signode, 'constinit')
if self.volatile:
addSpace = _add(signode, 'volatile')
if self.const:
@ -2235,7 +2292,7 @@ class ASTDeclSpecs(ASTBase):
env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode)
numChildren = len(signode)
self.leftSpecs.describe_signature(signode)
self.leftSpecs.describe_signature(signode, env, symbol)
addSpace = len(signode) != numChildren
if self.trailingTypeSpec:
@ -2249,7 +2306,7 @@ class ASTDeclSpecs(ASTBase):
if len(str(self.rightSpecs)) > 0:
if addSpace:
signode += addnodes.desc_sig_space()
self.rightSpecs.describe_signature(signode)
self.rightSpecs.describe_signature(signode, env, symbol)
# Declarator
@ -4942,8 +4999,8 @@ class DefinitionParser(BaseParser):
# those without signedness and size modifiers
# see https://en.cppreference.com/w/cpp/language/types
_simple_fundemental_types = (
'void', 'bool', 'char', 'wchar_t', 'char16_t', 'char32_t', 'int',
'float', 'double', 'auto'
'void', 'bool', 'char', 'wchar_t', 'char8_t', 'char16_t', 'char32_t',
'int', 'float', 'double', 'auto'
)
_prefix_keys = ('class', 'struct', 'enum', 'union', 'typename')
@ -5815,7 +5872,19 @@ class DefinitionParser(BaseParser):
prefix = k
break
nestedName = self._parse_nested_name()
return ASTTrailingTypeSpecName(prefix, nestedName)
self.skip_ws()
placeholderType = None
if self.skip_word('auto'):
placeholderType = 'auto'
elif self.skip_word_and_ws('decltype'):
if not self.skip_string_and_ws('('):
self.fail("Expected '(' after 'decltype' in placeholder type specifier.")
if not self.skip_word_and_ws('auto'):
self.fail("Expected 'auto' after 'decltype(' in placeholder type specifier.")
if not self.skip_string_and_ws(')'):
self.fail("Expected ')' after 'decltype(auto' in placeholder type specifier.")
placeholderType = 'decltype(auto)'
return ASTTrailingTypeSpecName(prefix, nestedName, placeholderType)
def _parse_parameters_and_qualifiers(self, paramMode: str) -> ASTParametersQualifiers:
if paramMode == 'new':
@ -5929,14 +5998,24 @@ class DefinitionParser(BaseParser):
threadLocal = None
inline = None
virtual = None
explicit = None
explicitSpec = None
consteval = None
constexpr = None
constinit = None
volatile = None
const = None
friend = None
attrs = []
while 1: # accept any permutation of a subset of some decl-specs
self.skip_ws()
if not const and typed:
const = self.skip_word('const')
if const:
continue
if not volatile and typed:
volatile = self.skip_word('volatile')
if volatile:
continue
if not storage:
if outer in ('member', 'function'):
if self.skip_word('static'):
@ -5952,16 +6031,28 @@ class DefinitionParser(BaseParser):
if self.skip_word('register'):
storage = 'register'
continue
if not threadLocal and outer == 'member':
threadLocal = self.skip_word('thread_local')
if threadLocal:
if not inline and outer in ('function', 'member'):
inline = self.skip_word('inline')
if inline:
continue
if not constexpr and outer in ('member', 'function'):
constexpr = self.skip_word("constexpr")
if constexpr:
continue
if outer == 'member':
if not constinit:
constinit = self.skip_word('constinit')
if constinit:
continue
if not threadLocal:
threadLocal = self.skip_word('thread_local')
if threadLocal:
continue
if outer == 'function':
# function-specifiers
if not inline:
inline = self.skip_word('inline')
if inline:
if not consteval:
consteval = self.skip_word('consteval')
if consteval:
continue
if not friend:
friend = self.skip_word('friend')
@ -5971,31 +6062,28 @@ class DefinitionParser(BaseParser):
virtual = self.skip_word('virtual')
if virtual:
continue
if not explicit:
explicit = self.skip_word('explicit')
if not explicitSpec:
explicit = self.skip_word_and_ws('explicit')
if explicit:
expr: ASTExpression = None
if self.skip_string('('):
expr = self._parse_constant_expression(inTemplate=False)
if not expr:
self.fail("Expected constant expression after '('" +
" in explicit specifier.")
self.skip_ws()
if not self.skip_string(')'):
self.fail("Expected ')' to end explicit specifier.")
explicitSpec = ASTExplicitSpec(expr)
continue
if not constexpr and outer in ('member', 'function'):
constexpr = self.skip_word("constexpr")
if constexpr:
continue
if not volatile and typed:
volatile = self.skip_word('volatile')
if volatile:
continue
if not const and typed:
const = self.skip_word('const')
if const:
continue
attr = self._parse_attribute()
if attr:
attrs.append(attr)
continue
break
return ASTDeclSpecsSimple(storage, threadLocal, inline, virtual,
explicit, constexpr, volatile, const,
friend, attrs)
explicitSpec, consteval, constexpr, constinit,
volatile, const, friend, attrs)
def _parse_decl_specs(self, outer: str, typed: bool = True) -> ASTDeclSpecs:
if outer:

View File

@ -33,10 +33,10 @@ identifier_re = re.compile(r'''(?x)
)
[a-zA-Z0-9_]*\b
''')
integer_literal_re = re.compile(r'[1-9][0-9]*')
octal_literal_re = re.compile(r'0[0-7]*')
hex_literal_re = re.compile(r'0[xX][0-9a-fA-F][0-9a-fA-F]*')
binary_literal_re = re.compile(r'0[bB][01][01]*')
integer_literal_re = re.compile(r'[1-9][0-9]*(\'[0-9]+)*')
octal_literal_re = re.compile(r'0[0-7]*(\'[0-7]+)*')
hex_literal_re = re.compile(r'0[xX][0-9a-fA-F]+(\'[0-9a-fA-F]+)*')
binary_literal_re = re.compile(r'0[bB][01]+(\'[01]+)*')
integers_literal_suffix_re = re.compile(r'''(?x)
# unsigned and/or (long) long, in any order, but at least one of them
(
@ -50,13 +50,14 @@ integers_literal_suffix_re = re.compile(r'''(?x)
float_literal_re = re.compile(r'''(?x)
[+-]?(
# decimal
([0-9]+[eE][+-]?[0-9]+)
| ([0-9]*\.[0-9]+([eE][+-]?[0-9]+)?)
| ([0-9]+\.([eE][+-]?[0-9]+)?)
([0-9]+(\'[0-9]+)*[eE][+-]?[0-9]+(\'[0-9]+)*)
| (([0-9]+(\'[0-9]+)*)?\.[0-9]+(\'[0-9]+)*([eE][+-]?[0-9]+(\'[0-9]+)*)?)
| ([0-9]+(\'[0-9]+)*\.([eE][+-]?[0-9]+(\'[0-9]+)*)?)
# hex
| (0[xX][0-9a-fA-F]+[pP][+-]?[0-9a-fA-F]+)
| (0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+([pP][+-]?[0-9a-fA-F]+)?)
| (0[xX][0-9a-fA-F]+\.([pP][+-]?[0-9a-fA-F]+)?)
| (0[xX][0-9a-fA-F]+(\'[0-9a-fA-F]+)*[pP][+-]?[0-9a-fA-F]+(\'[0-9a-fA-F]+)*)
| (0[xX]([0-9a-fA-F]+(\'[0-9a-fA-F]+)*)?\.
[0-9a-fA-F]+(\'[0-9a-fA-F]+)*([pP][+-]?[0-9a-fA-F]+(\'[0-9a-fA-F]+)*)?)
| (0[xX][0-9a-fA-F]+(\'[0-9a-fA-F]+)*\.([pP][+-]?[0-9a-fA-F]+(\'[0-9a-fA-F]+)*)?)
)
''')
float_literal_suffix_re = re.compile(r'[fFlL]\b')

View File

@ -155,7 +155,8 @@ def test_domain_c_ast_expressions():
# primary
exprCheck('true')
exprCheck('false')
ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1']
ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1',
"0b0'1'0", "00'1'2", "0x0'1'2", "1'2'3"]
unsignedSuffix = ['', 'u', 'U']
longSuffix = ['', 'l', 'L', 'll', 'LL']
for i in ints:
@ -170,14 +171,18 @@ def test_domain_c_ast_expressions():
'5e42', '5e+42', '5e-42',
'5.', '5.e42', '5.e+42', '5.e-42',
'.5', '.5e42', '.5e+42', '.5e-42',
'5.0', '5.0e42', '5.0e+42', '5.0e-42']:
'5.0', '5.0e42', '5.0e+42', '5.0e-42',
"1'2'3e7'8'9", "1'2'3.e7'8'9",
".4'5'6e7'8'9", "1'2'3.4'5'6e7'8'9"]:
expr = e + suffix
exprCheck(expr)
for e in [
'ApF', 'Ap+F', 'Ap-F',
'A.', 'A.pF', 'A.p+F', 'A.p-F',
'.A', '.ApF', '.Ap+F', '.Ap-F',
'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F']:
'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F',
"A'B'Cp1'2'3", "A'B'C.p1'2'3",
".D'E'Fp1'2'3", "A'B'C.D'E'Fp1'2'3"]:
expr = "0x" + e + suffix
exprCheck(expr)
exprCheck('"abc\\"cba"') # string

View File

@ -126,6 +126,7 @@ def test_domain_cpp_ast_fundamental_types():
id = t.replace(" ", "-").replace("long", "l").replace("int", "i")
id = id.replace("bool", "b").replace("char", "c")
id = id.replace("wc_t", "wchar_t").replace("c16_t", "char16_t")
id = id.replace("c8_t", "char8_t")
id = id.replace("c32_t", "char32_t")
return "f__%s" % id
@ -173,7 +174,8 @@ def test_domain_cpp_ast_expressions():
exprCheck('nullptr', 'LDnE')
exprCheck('true', 'L1E')
exprCheck('false', 'L0E')
ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1']
ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1',
"0b0'1'0", "00'1'2", "0x0'1'2", "1'2'3"]
unsignedSuffix = ['', 'u', 'U']
longSuffix = ['', 'l', 'L', 'll', 'LL']
for i in ints:
@ -186,11 +188,15 @@ def test_domain_cpp_ast_expressions():
decimalFloats = ['5e42', '5e+42', '5e-42',
'5.', '5.e42', '5.e+42', '5.e-42',
'.5', '.5e42', '.5e+42', '.5e-42',
'5.0', '5.0e42', '5.0e+42', '5.0e-42']
'5.0', '5.0e42', '5.0e+42', '5.0e-42',
"1'2'3e7'8'9", "1'2'3.e7'8'9",
".4'5'6e7'8'9", "1'2'3.4'5'6e7'8'9"]
hexFloats = ['ApF', 'Ap+F', 'Ap-F',
'A.', 'A.pF', 'A.p+F', 'A.p-F',
'.A', '.ApF', '.Ap+F', '.Ap-F',
'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F']
'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F',
"A'B'Cp1'2'3", "A'B'C.p1'2'3",
".D'E'Fp1'2'3", "A'B'C.D'E'Fp1'2'3"]
for suffix in ['', 'f', 'F', 'l', 'L']:
for e in decimalFloats:
expr = e + suffix
@ -435,6 +441,9 @@ def test_domain_cpp_ast_member_definitions():
# check('member', 'int b : (true ? 8 : a) = 42', {1: 'b__i', 2: '1b'})
check('member', 'int b : 1 || new int{0}', {1: 'b__i', 2: '1b'})
check('member', 'inline int n', {1: 'n__i', 2: '1n'})
check('member', 'constinit int n', {1: 'n__i', 2: '1n'})
def test_domain_cpp_ast_function_definitions():
check('function', 'void f(volatile int)', {1: "f__iV", 2: "1fVi"})
@ -565,6 +574,9 @@ def test_domain_cpp_ast_function_definitions():
check("function", "void f(int *volatile const p)", {1: "f__iPVC", 2: "1fPVCi"})
check('function', 'extern int f()', {1: 'f', 2: '1fv'})
check('function', 'consteval int f()', {1: 'f', 2: '1fv'})
check('function', 'explicit(true) void f()', {1: 'f', 2: '1fv'})
check('function', 'decltype(auto) f()', {1: 'f', 2: "1fv"})
@ -854,6 +866,15 @@ def test_domain_cpp_ast_templates():
check('type', 'template<C T = int&> {key}A', {2: 'I_1CE1A'}, key='using')
def test_domain_cpp_ast_placeholder_types():
check('function', 'void f(Sortable auto &v)', {1: 'f__SortableR', 2: '1fR8Sortable'})
check('function', 'void f(const Sortable auto &v)', {1: 'f__SortableCR', 2: '1fRK8Sortable'})
check('function', 'void f(Sortable decltype(auto) &v)', {1: 'f__SortableR', 2: '1fR8Sortable'})
check('function', 'void f(const Sortable decltype(auto) &v)', {1: 'f__SortableCR', 2: '1fRK8Sortable'})
check('function', 'void f(Sortable decltype ( auto ) &v)', {1: 'f__SortableR', 2: '1fR8Sortable'},
output='void f(Sortable decltype(auto) &v)')
def test_domain_cpp_ast_requires_clauses():
check('function', 'template<typename T> requires A auto f() -> void requires B',
{4: 'I0EIQaa1A1BE1fvv'})