CPlusPlus: Handle C++20 concepts in parser

Change-Id: I8c6b8b1ba3f36b83cd1d667bec9830271147b1ac
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Christian Kandeler
2023-02-09 15:11:14 +01:00
parent 89a8631784
commit 61de69ea90
15 changed files with 547 additions and 1 deletions

View File

@@ -1255,6 +1255,8 @@ bool Parser::parseTemplateDeclaration(DeclarationAST *&node)
ast->declaration = nullptr;
if (parseDeclaration(ast->declaration))
break;
if (parseConceptDeclaration(ast->declaration))
break;
error(start_declaration, "expected a declaration");
rewind(start_declaration + 1);
@@ -1265,6 +1267,173 @@ bool Parser::parseTemplateDeclaration(DeclarationAST *&node)
return true;
}
bool Parser::parseConceptDeclaration(DeclarationAST *&node)
{
if (!_languageFeatures.cxx20Enabled)
return false;
if (LA() != T_CONCEPT)
return false;
const auto ast = new (_pool) ConceptDeclarationAST;
ast->concept_token = consumeToken();
if (!parseName(ast->name))
return false;
parseAttributeSpecifier(ast->attributes);
if (LA() != T_EQUAL)
return false;
ast->equals_token = consumeToken();
if (!parseLogicalOrExpression(ast->constraint))
return false;
if (LA() != T_SEMICOLON)
return false;
ast->semicolon_token = consumeToken();
node = ast;
return true;
}
bool Parser::parsePlaceholderTypeSpecifier(PlaceholderTypeSpecifierAST *&node)
{
if ((lookAtBuiltinTypeSpecifier() || _translationUnit->tokenAt(_tokenIndex).isKeyword())
&& (LA() != T_AUTO && LA() != T_DECLTYPE)) {
return false;
}
TypeConstraintAST *typeConstraint = nullptr;
const int savedCursor = cursor();
parseTypeConstraint(typeConstraint);
if (LA() != T_AUTO && (LA() != T_DECLTYPE || LA(1) != T_LPAREN || LA(2) != T_AUTO)) {
rewind(savedCursor);
return false;
}
const auto spec = new (_pool) PlaceholderTypeSpecifierAST;
spec->typeConstraint = typeConstraint;
if (LA() == T_DECLTYPE) {
spec->declTypetoken = consumeToken();
if (LA() != T_LPAREN)
return false;
spec->lparenToken = consumeToken();
if (LA() != T_AUTO)
return false;
spec->autoToken = consumeToken();
if (LA() != T_RPAREN)
return false;
spec->rparenToken = consumeToken();
} else {
spec->autoToken = consumeToken();
}
node = spec;
return true;
}
bool Parser::parseTypeConstraint(TypeConstraintAST *&node)
{
NestedNameSpecifierListAST *nestedName = nullptr;
parseNestedNameSpecifierOpt(nestedName, true);
NameAST *conceptName = nullptr;
if (!parseUnqualifiedName(conceptName, true))
return false;
const auto typeConstraint = new (_pool) TypeConstraintAST;
typeConstraint->nestedName = nestedName;
typeConstraint->conceptName = conceptName;
if (LA() != T_LESS)
return true;
typeConstraint->lessToken = consumeToken();
if (LA() != T_GREATER) {
if (!parseTemplateArgumentList(typeConstraint->templateArgs))
return false;
}
if (LA() != T_GREATER)
return false;
typeConstraint->greaterToken = consumeToken();
node = typeConstraint;
return true;
}
bool Parser::parseRequirement()
{
if (LA() == T_TYPENAME) { // type-requirement
consumeToken();
NameAST *name = nullptr;
if (!parseName(name, true))
return false;
if (LA() != T_SEMICOLON)
return false;
consumeToken();
return true;
}
if (LA() == T_LBRACE) { // compound-requirement
consumeToken();
ExpressionAST *expr = nullptr;
if (!parseExpression(expr))
return false;
if (LA() != T_RBRACE)
return false;
consumeToken();
if (LA() == T_NOEXCEPT)
consumeToken();
if (LA() == T_SEMICOLON) {
consumeToken();
return true;
}
TypeConstraintAST *typeConstraint = nullptr;
if (!parseTypeConstraint(typeConstraint))
return false;
if (LA() != T_SEMICOLON)
return false;
consumeToken();
return true;
}
if (LA() == T_REQUIRES) { // nested-requirement
consumeToken();
ExpressionAST *constraintExpr = nullptr;
if (!parseLogicalOrExpression(constraintExpr))
return false;
if (LA() != T_SEMICOLON)
return false;
consumeToken();
return true;
}
ExpressionAST *simpleExpr;
if (!parseExpression(simpleExpr)) // simple-requirement
return false;
if (LA() != T_SEMICOLON)
return false;
consumeToken();
return true;
}
bool Parser::parseRequiresExpression(ExpressionAST *&node)
{
if (!_languageFeatures.cxx20Enabled)
return false;
if (LA() != T_REQUIRES)
return false;
const auto ast = new (_pool) RequiresExpressionAST;
ast->requires_token = consumeToken();
if (LA() == T_LPAREN) {
ast->lparen_token = consumeToken();
if (!parseParameterDeclarationClause(ast->parameters))
return false;
if (LA() != T_RPAREN)
return false;
ast->rparen_token = consumeToken();
}
if (LA() != T_LBRACE)
return false;
ast->lbrace_token = consumeToken();
if (!parseRequirement())
return false;
while (LA() != T_RBRACE) {
if (!parseRequirement())
return false;
}
ast->rbrace_token = consumeToken();
node = ast;
return true;
}
bool Parser::parseOperator(OperatorAST *&node) // ### FIXME
{
DEBUG_THIS_RULE();
@@ -1500,6 +1669,14 @@ bool Parser::parseDeclSpecifierSeq(SpecifierListAST *&decl_specifier_seq,
NameAST *named_type_specifier = nullptr;
SpecifierListAST **decl_specifier_seq_ptr = &decl_specifier_seq;
for (;;) {
PlaceholderTypeSpecifierAST *placeholderSpec = nullptr;
// A simple auto is also technically a placeholder-type-specifier, but for historical
// reasons, it is handled further below.
if (LA() != T_AUTO && parsePlaceholderTypeSpecifier(placeholderSpec)) {
*decl_specifier_seq_ptr = new (_pool) SpecifierListAST(placeholderSpec);
decl_specifier_seq_ptr = &(*decl_specifier_seq_ptr)->next;
continue;
}
if (! noStorageSpecifiers && ! onlySimpleTypeSpecifiers && lookAtStorageClassSpecifier()) {
// storage-class-specifier
SimpleSpecifierAST *spec = new (_pool) SimpleSpecifierAST;
@@ -1550,8 +1727,9 @@ bool Parser::parseDeclSpecifierSeq(SpecifierListAST *&decl_specifier_seq,
}
decl_specifier_seq_ptr = &(*decl_specifier_seq_ptr)->next;
has_type_specifier = true;
} else
} else {
break;
}
}
return decl_specifier_seq != nullptr;
@@ -1694,6 +1872,8 @@ bool Parser::hasAuto(SpecifierListAST *decl_specifier_list) const
if (_translationUnit->tokenKind(simpleSpec->specifier_token) == T_AUTO)
return true;
}
if (spec->asPlaceholderTypeSpecifier())
return true;
}
return false;
}
@@ -4731,6 +4911,9 @@ bool Parser::parsePrimaryExpression(ExpressionAST *&node)
case T_AT_SELECTOR:
return parseObjCExpression(node);
case T_REQUIRES:
return parseRequiresExpression(node);
default: {
NameAST *name = nullptr;
if (parseNameId(name)) {