diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 5d3cd1e6..7ddcf7c4 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -482,6 +482,23 @@ struct Parser::ClassHead { SourceLocation finalLoc; SourceLocation colonLoc; List* baseSpecifierList = nullptr; + + [[nodiscard]] auto location() const -> SourceLocation { + if (name) return name->firstSourceLocation(); + return classLoc; + } + + [[nodiscard]] auto className() const -> const Identifier* { + if (auto templateId = ast_cast(name)) { + return templateId->identifier; + } + + if (auto nameId = ast_cast(name)) { + return nameId->identifier; + } + + return nullptr; + } }; struct Parser::DeclSpecs { @@ -6533,30 +6550,35 @@ auto Parser::evaluate_constant_expression(ExpressionAST* expr) return sem.evaluate(expr); } -auto Parser::parse_elaborated_type_specifier( - SpecifierAST*& yyast, DeclSpecs& specs, - const std::vector& templateDeclarations) -> bool { - if (specs.typeSpecifier) return false; - - if (!LA().isOneOf(TokenKind::T_ENUM, TokenKind::T_CLASS, TokenKind::T_STRUCT, - TokenKind::T_UNION)) - return false; - - const auto start = currentLocation(); +auto Parser::parse_elaborated_enum_specifier(SpecifierAST*& yyast, + DeclSpecs& specs) -> bool { + SourceLocation enumLoc; + if (!match(TokenKind::T_ENUM, enumLoc)) return false; - ElaboratedTypeSpecifierAST* ast = nullptr; + NestedNameSpecifierAST* nestedNameSpecifier = nullptr; + parse_optional_nested_name_specifier(nestedNameSpecifier); - const auto parsed = - parse_elaborated_type_specifier_helper(ast, specs, templateDeclarations); + NameIdAST* name = nullptr; + if (!parse_name_id(name)) { + parse_error("expected a name"); + } + auto ast = make_node(pool_); yyast = ast; - return parsed; + ast->classLoc = enumLoc; + ast->nestedNameSpecifier = nestedNameSpecifier; + ast->unqualifiedId = name; + ast->classKey = TokenKind::T_ENUM; + + return true; } -auto Parser::parse_elaborated_type_specifier_helper( - ElaboratedTypeSpecifierAST*& yyast, DeclSpecs& specs, +auto Parser::parse_elaborated_type_specifier( + SpecifierAST*& yyast, DeclSpecs& specs, const std::vector& templateDeclarations) -> bool { + if (specs.typeSpecifier) return false; + if (parse_elaborated_enum_specifier(yyast, specs)) return true; SourceLocation classLoc; @@ -6571,6 +6593,10 @@ auto Parser::parse_elaborated_type_specifier_helper( SourceLocation templateLoc; const auto isTemplateIntroduced = match(TokenKind::T_TEMPLATE, templateLoc); + if (!lookat(TokenKind::T_IDENTIFIER)) return false; + + const auto classKey = unit->tokenKind(classLoc); + auto ast = make_node(pool_); yyast = ast; @@ -6581,63 +6607,54 @@ auto Parser::parse_elaborated_type_specifier_helper( ast->classKey = unit->tokenKind(classLoc); ast->isTemplateIntroduced = isTemplateIntroduced; - const auto loc = currentLocation(); + const Identifier* className = nullptr; + SimpleTemplateIdAST* templateId = nullptr; + + if (parse_simple_template_id(templateId, ast->nestedNameSpecifier, + ast->isTemplateIntroduced)) { + ast->unqualifiedId = templateId; + className = templateId->identifier; + } else { + // if we reach here, we have a name-id + NameIdAST* nameId = nullptr; + (void)parse_name_id(nameId); - if (lookat(TokenKind::T_IDENTIFIER, TokenKind::T_LESS)) { - if (SimpleTemplateIdAST* templateId = nullptr; parse_simple_template_id( - templateId, nestedNameSpecifier, isTemplateIntroduced)) { - ast->unqualifiedId = templateId; - } else { - parse_error(loc, "expected a template-id"); - } - } else if (NameIdAST* nameId = nullptr; parse_name_id(nameId)) { ast->unqualifiedId = nameId; + className = nameId->identifier; + } - auto symbol = Lookup{scope_}(nestedNameSpecifier, nameId->identifier); + const auto location = ast->unqualifiedId->firstSourceLocation(); - if (ast->classKey == TokenKind::T_CLASS || - ast->classKey == TokenKind::T_STRUCT || - ast->classKey == TokenKind::T_UNION) { - for (auto symbol : SymbolChainView(symbol) | views::classes) { - ast->symbol = symbol; - specs.type = symbol->type(); - break; - } - } else if (ast->classKey == TokenKind::T_ENUM) { - for (auto symbol : - SymbolChainView(symbol) | views::enum_or_scoped_enums) { - ast->symbol = symbol; - specs.type = symbol->type(); - break; - } + const auto _ = ScopeGuard{this}; + + if (ast->nestedNameSpecifier) { + auto parent = ast->nestedNameSpecifier->symbol; + + if (parent && parent->isClassOrNamespace()) { + setScope(static_cast(parent)); } - } else { - parse_error(loc, "expected a name"); } - return true; -} + ClassSymbol* classSymbol = nullptr; -auto Parser::parse_elaborated_enum_specifier(ElaboratedTypeSpecifierAST*& yyast, - DeclSpecs& specs) -> bool { - SourceLocation enumLoc; - if (!match(TokenKind::T_ENUM, enumLoc)) return false; + if (scope_->isClassOrNamespaceScope()) { + for (auto candidate : scope_->find(className) | views::classes) { + classSymbol = candidate; + break; + } + } - NestedNameSpecifierAST* nestedNameSpecifier = nullptr; - parse_optional_nested_name_specifier(nestedNameSpecifier); + if (!classSymbol) { + const auto isUnion = classKey == TokenKind::T_UNION; + classSymbol = control_->newClassSymbol(scope_, location); - NameIdAST* name = nullptr; - if (!parse_name_id(name)) { - parse_error("expected a name"); + classSymbol->setIsUnion(isUnion); + classSymbol->setName(className); + classSymbol->setTemplateParameters(currentTemplateParameters()); + declaringScope()->addSymbol(classSymbol); } - auto ast = make_node(pool_); - yyast = ast; - - ast->classLoc = enumLoc; - ast->nestedNameSpecifier = nestedNameSpecifier; - ast->unqualifiedId = name; - ast->classKey = TokenKind::T_ENUM; + ast->symbol = classSymbol; return true; } @@ -7906,10 +7923,13 @@ auto Parser::parse_using_enum_declaration(DeclarationAST*& yyast) -> bool { DeclSpecs specs{this}; - if (!parse_elaborated_enum_specifier(ast->enumTypeSpecifier, specs)) { + SpecifierAST* typeSpecifier = nullptr; + if (!parse_elaborated_type_specifier(typeSpecifier, specs, {})) { parse_error("expected an elaborated enum specifier"); } + ast->enumTypeSpecifier = ast_cast(typeSpecifier); + expect(TokenKind::T_SEMICOLON, ast->semicolonLoc); return true; @@ -9023,28 +9043,10 @@ auto Parser::parse_class_specifier(ClassSpecifierAST*& yyast, DeclSpecs& specs) auto Parser::parse_class_specifier( ClassSpecifierAST*& yyast, DeclSpecs& specs, const std::vector& templateDeclarations) -> bool { - if (!LA().isOneOf(TokenKind::T_CLASS, TokenKind::T_STRUCT, - TokenKind::T_UNION)) - return false; - ScopeGuard scopeGuard{this}; ClassHead classHead{templateDeclarations}; - - auto lookat_class_specifier = [&] { - LookaheadParser lookahead{this}; - - if (parse_class_head(classHead)) { - if (classHead.colonLoc || lookat(TokenKind::T_LBRACE)) { - lookahead.commit(); - return true; - } - } - - return false; - }; - - if (!lookat_class_specifier()) return false; + if (!parse_class_head(classHead)) return false; SourceLocation lbraceLoc; expect(TokenKind::T_LBRACE, lbraceLoc); @@ -9075,6 +9077,8 @@ auto Parser::parse_class_specifier( expect(TokenKind::T_RBRACE, ast->rbraceLoc); } + ast->symbol->setComplete(true); + return true; } @@ -9106,6 +9110,8 @@ void Parser::parse_class_body(List*& yyast) { } auto Parser::parse_class_head(ClassHead& classHead) -> bool { + LookaheadParser lookahead{this}; + if (!parse_class_key(classHead.classLoc)) return false; const auto isUnion = @@ -9113,96 +9119,80 @@ auto Parser::parse_class_head(ClassHead& classHead) -> bool { parse_optional_attribute_specifier_seq(classHead.attributeList); - auto is_class_declaration = false; - if (parse_class_head_name(classHead.nestedNameSpecifier, classHead.name)) { - if (parse_class_virt_specifier(classHead.finalLoc)) { - is_class_declaration = true; - } + (void)parse_class_virt_specifier(classHead.finalLoc); } - if (LA().isOneOf(TokenKind::T_COLON, TokenKind::T_LBRACE)) { - is_class_declaration = true; + if (!LA().isOneOf(TokenKind::T_COLON, TokenKind::T_LBRACE)) { + return false; } - auto is_template_declaration = !classHead.templateDeclarations.empty(); + lookahead.commit(); - if (is_class_declaration && is_template_declaration && - !classHead.nestedNameSpecifier) { + if (scope_->isTemplateParametersScope()) { mark_maybe_template_name(classHead.name); } - if (classHead.nestedNameSpecifier) { - auto enclosingSymbol = classHead.nestedNameSpecifier->symbol; - if (!enclosingSymbol) { - if (config_.checkTypes) { - parse_error(classHead.nestedNameSpecifier->firstSourceLocation(), - "unresolved nested name specifier"); - } - } else { - Scope* enclosingScope = nullptr; + SimpleTemplateIdAST* templateId = + ast_cast(classHead.name); - if (auto alias = symbol_cast(enclosingSymbol)) { - if (auto classTy = type_cast(alias->type())) { - enclosingScope = classTy->symbol()->scope(); - } - } else if (auto enclosingClass = - symbol_cast(enclosingSymbol)) { - enclosingScope = enclosingClass->scope(); - } else if (auto enclosingNamespace = - symbol_cast(enclosingSymbol)) { - enclosingScope = enclosingNamespace->scope(); - } + bool isTemplateSpecialization = false; + if (templateId) isTemplateSpecialization = true; - if (enclosingScope) { - setScope(enclosingScope); - } else if (config_.checkTypes) { - parse_error(classHead.nestedNameSpecifier->firstSourceLocation(), - "unresolved nested name specifier"); - } + const auto className = classHead.className(); + const auto location = classHead.location(); + + auto templateParameters = currentTemplateParameters(); + + if (classHead.nestedNameSpecifier) { + auto parent = classHead.nestedNameSpecifier->symbol; + + if (parent && parent->isClassOrNamespace()) { + setScope(static_cast(parent)); } } - const Identifier* identifier = nullptr; - SourceLocation location; - bool isTemplateSpecialization = false; - if (const auto simpleName = ast_cast(classHead.name)) { - location = simpleName->identifierLoc; - identifier = simpleName->identifier; - } else if (const auto t = ast_cast(classHead.name)) { - location = t->firstSourceLocation(); - isTemplateSpecialization = true; - identifier = t->identifier; - } else { - location = currentLocation(); + ClassSymbol* primaryTemplate = nullptr; + + if (templateId && scope_->isTemplateParametersScope()) { + for (auto candidate : declaringScope()->find(className) | views::classes) { + primaryTemplate = candidate; + break; + } + + if (!primaryTemplate && config_.checkTypes) { + parse_error(location, + std::format("specialization of undeclared template '{}'", + className->name())); + } } ClassSymbol* classSymbol = nullptr; - if (identifier) { - if (!is_class_declaration && !lookat(TokenKind::T_SEMICOLON)) { - auto symbol = symbol_cast(Lookup{scope_}(identifier)); - classSymbol = symbol; - } else if (!isTemplateSpecialization) { - for (auto previousClass : scope_->find(identifier) | views::classes) { - if (previousClass->isComplete()) { - parse_error(classHead.name->firstSourceLocation(), - "class name already declared"); - } else { - classSymbol = previousClass; - } - break; - } + if (className) { + for (auto candidate : declaringScope()->find(className) | views::classes) { + classSymbol = candidate; + break; } } + if (classSymbol && classSymbol->isComplete()) { + classSymbol = nullptr; + } + if (!classSymbol) { classSymbol = control_->newClassSymbol(scope_, location); classSymbol->setIsUnion(isUnion); - classSymbol->setName(identifier); - classSymbol->setTemplateParameters(currentTemplateParameters()); + classSymbol->setName(className); + classSymbol->setTemplateParameters(templateParameters); - declaringScope()->addSymbol(classSymbol); + if (!primaryTemplate) { + declaringScope()->addSymbol(classSymbol); + } else { + std::vector arguments; + // TODO: parse template arguments + primaryTemplate->addSpecialization(arguments, classSymbol); + } } classHead.symbol = classSymbol; @@ -9225,14 +9215,10 @@ auto Parser::parse_class_head_name(NestedNameSpecifierAST*& nestedNameSpecifier, check_type_traits(); - UnqualifiedIdAST* name = nullptr; - - if (!parse_type_name(name, nestedNameSpecifier, + if (!parse_type_name(yyast, nestedNameSpecifier, /*isTemplateIntroduced*/ false)) return false; - yyast = name; - return true; } diff --git a/src/parser/cxx/parser.h b/src/parser/cxx/parser.h index 2ae49c58..26e4cb0a 100644 --- a/src/parser/cxx/parser.h +++ b/src/parser/cxx/parser.h @@ -459,11 +459,8 @@ class Parser final { [[nodiscard]] auto parse_elaborated_type_specifier( SpecifierAST*& yyast, DeclSpecs& specs, const std::vector& templateDeclarations) -> bool; - [[nodiscard]] auto parse_elaborated_type_specifier_helper( - ElaboratedTypeSpecifierAST*& yyast, DeclSpecs& specs, - const std::vector& templateDeclarations) -> bool; - [[nodiscard]] auto parse_elaborated_enum_specifier( - ElaboratedTypeSpecifierAST*& yyast, DeclSpecs& specs) -> bool; + [[nodiscard]] auto parse_elaborated_enum_specifier(SpecifierAST*& yyast, + DeclSpecs& specs) -> bool; [[nodiscard]] auto parse_decltype_specifier(DecltypeSpecifierAST*& yyast) -> bool; [[nodiscard]] auto parse_placeholder_type_specifier(SpecifierAST*& yyast, diff --git a/src/parser/cxx/scope.cc b/src/parser/cxx/scope.cc index b1e82edb..8092f975 100644 --- a/src/parser/cxx/scope.cc +++ b/src/parser/cxx/scope.cc @@ -55,6 +55,16 @@ auto Scope::isNamespaceScope() const -> bool { return owner_ && owner_->isNamespace(); } +auto Scope::isClassScope() const -> bool { return owner_ && owner_->isClass(); } + +auto Scope::isClassOrNamespaceScope() const -> bool { + return isClassScope() || isNamespaceScope(); +} + +auto Scope::isFunctionScope() const -> bool { + return owner_ && owner_->isFunction(); +} + auto Scope::isBlockScope() const -> bool { return owner_ && owner_->isBlock(); } auto Scope::isEnumScope() const -> bool { diff --git a/src/parser/cxx/scope.h b/src/parser/cxx/scope.h index 76392c49..9b4fea7d 100644 --- a/src/parser/cxx/scope.h +++ b/src/parser/cxx/scope.h @@ -39,6 +39,9 @@ class Scope { [[nodiscard]] auto isTransparent() const -> bool; [[nodiscard]] auto isNamespaceScope() const -> bool; + [[nodiscard]] auto isClassScope() const -> bool; + [[nodiscard]] auto isClassOrNamespaceScope() const -> bool; + [[nodiscard]] auto isFunctionScope() const -> bool; [[nodiscard]] auto isBlockScope() const -> bool; [[nodiscard]] auto isEnumScope() const -> bool; [[nodiscard]] auto isTemplateParametersScope() const -> bool;