From ebd53276c0a2f6a03c2707291c0678e74d1f0ce6 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Sat, 8 Feb 2025 10:28:30 +0100 Subject: [PATCH] Resolve references to primary templates Signed-off-by: Roberto Raggi --- src/parser/cxx/parser.cc | 275 ++++++++++++++------- src/parser/cxx/parser.h | 18 +- tests/unit_tests/sema/template_alias_01.cc | 10 +- tests/unit_tests/sema/template_class_03.cc | 41 +++ 4 files changed, 247 insertions(+), 97 deletions(-) create mode 100644 tests/unit_tests/sema/template_class_03.cc diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 76e4efa2..9f6941c8 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -1387,7 +1387,8 @@ auto Parser::parse_id_expression(IdExpressionAST*& yyast, LookaheadParser lookahead{this}; NestedNameSpecifierAST* nestedNameSpecifier = nullptr; - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kNonDeclarative); SourceLocation templateLoc; const auto isTemplateIntroduced = match(TokenKind::T_TEMPLATE, templateLoc); @@ -1493,119 +1494,179 @@ auto Parser::parse_unqualified_id(UnqualifiedIdAST*& yyast, } void Parser::parse_optional_nested_name_specifier( - NestedNameSpecifierAST*& yyast) { + NestedNameSpecifierAST*& yyast, NestedNameSpecifierContext ctx) { LookaheadParser lookahead(this); - if (!parse_nested_name_specifier(yyast)) return; + if (!parse_nested_name_specifier(yyast, ctx)) return; lookahead.commit(); } -auto Parser::parse_nested_name_specifier(NestedNameSpecifierAST*& yyast) - -> bool { - auto lookat_decltype_nested_name_specifier = [&] { - LookaheadParser lookahead{this}; +auto Parser::parse_decltype_nested_name_specifier( + NestedNameSpecifierAST*& yyast, NestedNameSpecifierContext ctx) -> bool { + LookaheadParser lookahead{this}; - SourceLocation decltypeLoc; - if (!match(TokenKind::T_DECLTYPE, decltypeLoc)) return false; - if (!lookat(TokenKind::T_LPAREN)) return false; - if (!parse_skip_balanced()) return false; - if (!lookat(TokenKind::T_COLON_COLON)) return false; + SourceLocation decltypeLoc; + if (!match(TokenKind::T_DECLTYPE, decltypeLoc)) return false; + if (!lookat(TokenKind::T_LPAREN)) return false; + if (!parse_skip_balanced()) return false; + if (!lookat(TokenKind::T_COLON_COLON)) return false; - rewind(decltypeLoc); + rewind(decltypeLoc); - DecltypeSpecifierAST* decltypeSpecifier = nullptr; - if (!parse_decltype_specifier(decltypeSpecifier)) return false; + DecltypeSpecifierAST* decltypeSpecifier = nullptr; + if (!parse_decltype_specifier(decltypeSpecifier)) return false; - SourceLocation scopeLoc; - if (!match(TokenKind::T_COLON_COLON, scopeLoc)) return false; + SourceLocation scopeLoc; + if (!match(TokenKind::T_COLON_COLON, scopeLoc)) return false; - lookahead.commit(); + lookahead.commit(); - auto ast = make_node(pool_); - yyast = ast; + auto ast = make_node(pool_); + yyast = ast; - ast->decltypeSpecifier = decltypeSpecifier; - ast->scopeLoc = scopeLoc; + ast->decltypeSpecifier = decltypeSpecifier; + ast->scopeLoc = scopeLoc; - if (decltypeSpecifier) { - if (auto classsType = type_cast(decltypeSpecifier->type)) { - ast->symbol = classsType->symbol(); - } else if (auto enumType = type_cast(decltypeSpecifier->type)) { - ast->symbol = enumType->symbol(); - } else if (auto scopedEnumType = - type_cast(decltypeSpecifier->type)) { - ast->symbol = scopedEnumType->symbol(); - } + if (decltypeSpecifier) { + if (auto classsType = type_cast(decltypeSpecifier->type)) { + ast->symbol = classsType->symbol(); + } else if (auto enumType = type_cast(decltypeSpecifier->type)) { + ast->symbol = enumType->symbol(); + } else if (auto scopedEnumType = + type_cast(decltypeSpecifier->type)) { + ast->symbol = scopedEnumType->symbol(); } + } - return true; - }; + return true; +} - auto lookat_simple_nested_name_specifier = [&] { - if (!lookat(TokenKind::T_IDENTIFIER, TokenKind::T_COLON_COLON)) - return false; +auto Parser::parse_type_nested_name_specifier(NestedNameSpecifierAST*& yyast, + NestedNameSpecifierContext ctx) + -> bool { + if (!lookat(TokenKind::T_IDENTIFIER, TokenKind::T_COLON_COLON)) return false; - auto identifierLoc = consumeToken(); - auto identifier = unit->identifier(identifierLoc); - auto scopeLoc = consumeToken(); - auto symbol = Lookup{scope_}.lookupType(yyast, identifier); + auto identifierLoc = consumeToken(); + auto identifier = unit->identifier(identifierLoc); + auto scopeLoc = consumeToken(); + auto symbol = Lookup{scope_}.lookupType(yyast, identifier); - auto ast = make_node(pool_); - ast->nestedNameSpecifier = yyast; - yyast = ast; + auto ast = make_node(pool_); + ast->nestedNameSpecifier = yyast; + yyast = ast; - ast->identifierLoc = identifierLoc; - ast->identifier = identifier; - ast->scopeLoc = scopeLoc; - ast->symbol = symbol; + ast->identifierLoc = identifierLoc; + ast->identifier = identifier; + ast->scopeLoc = scopeLoc; + ast->symbol = symbol; + + return true; +} + +struct IsReferencingTemplateParameter { + Parser& p; + int depth = 0; + int index = 0; + + auto operator()(TypeTemplateArgumentAST* ast) const -> bool { + auto typeId = ast->typeId; + if (!typeId) return false; + + if (checkTypeParam(typeId->type)) return true; + + return false; + } + + [[nodiscard]] auto checkTypeParam(const Type* type) const -> bool { + auto typeParam = type_cast(type); + if (!typeParam) return false; + + auto typeParamSymbol = typeParam->symbol(); + if (typeParamSymbol->depth() != depth) return false; + if (typeParamSymbol->index() != index) return false; return true; - }; + } - auto lookat_template_nested_name_specifier = [&] { - LookaheadParser lookahead{this}; + auto operator()(ExpressionTemplateArgumentAST* ast) const -> bool { + // ### TODO + return false; + } +}; - SourceLocation templateLoc; - const auto isTemplateIntroduced = match(TokenKind::T_TEMPLATE, templateLoc); +auto Parser::parse_template_nested_name_specifier( + NestedNameSpecifierAST*& yyast, NestedNameSpecifierContext ctx, int depth) + -> bool { + LookaheadParser lookahead{this}; - SimpleTemplateIdAST* templateName = nullptr; - if (!parse_simple_template_id(templateName, yyast, isTemplateIntroduced)) - return false; + SourceLocation templateLoc; + const auto isTemplateIntroduced = match(TokenKind::T_TEMPLATE, templateLoc); - SourceLocation scopeLoc; - if (!match(TokenKind::T_COLON_COLON, scopeLoc)) return false; + SimpleTemplateIdAST* templateId = nullptr; + if (!parse_simple_template_id(templateId, yyast, isTemplateIntroduced)) + return false; - lookahead.commit(); + SourceLocation scopeLoc; + if (!match(TokenKind::T_COLON_COLON, scopeLoc)) return false; - auto ast = make_node(pool_); - ast->nestedNameSpecifier = yyast; - yyast = ast; + lookahead.commit(); - ast->templateLoc = templateLoc; - ast->templateId = templateName; - ast->scopeLoc = scopeLoc; - ast->isTemplateIntroduced = isTemplateIntroduced; - ast->symbol = instantiate(templateName); + auto ast = make_node(pool_); + ast->nestedNameSpecifier = yyast; + yyast = ast; - return true; - }; + ast->templateLoc = templateLoc; + ast->templateId = templateId; + ast->scopeLoc = scopeLoc; + ast->isTemplateIntroduced = isTemplateIntroduced; - yyast = nullptr; + if (ctx == NestedNameSpecifierContext::kDeclarative) { + bool isReferencingPrimaryTemplate = true; + for (int index = 0; auto arg : ListView{templateId->templateArgumentList}) { + if (!visit(IsReferencingTemplateParameter{*this, depth, index}, arg)) { + isReferencingPrimaryTemplate = false; + break; + } + ++index; + } + + if (isReferencingPrimaryTemplate) { + ast->symbol = templateId->primaryTemplateSymbol; + } + } + + if (!ast->symbol) { + ast->symbol = instantiate(templateId); + } + + return true; +} + +auto Parser::parse_nested_name_specifier(NestedNameSpecifierAST*& yyast, + NestedNameSpecifierContext ctx) + -> bool { if (SourceLocation scopeLoc; match(TokenKind::T_COLON_COLON, scopeLoc)) { auto ast = make_node(pool_); yyast = ast; ast->scopeLoc = scopeLoc; ast->symbol = globalScope_->owner(); - } else if (lookat_decltype_nested_name_specifier()) { + } else if (parse_decltype_nested_name_specifier(yyast, ctx)) { // } + int depth = 0; + while (true) { - if (lookat_simple_nested_name_specifier()) continue; - if (lookat_template_nested_name_specifier()) + if (parse_type_nested_name_specifier(yyast, ctx)) { continue; - else - break; + } + + if (parse_template_nested_name_specifier(yyast, ctx, depth)) { + ++depth; + continue; + } + + break; } const auto parsed = yyast != nullptr; @@ -1616,7 +1677,6 @@ auto Parser::parse_nested_name_specifier(NestedNameSpecifierAST*& yyast) auto Parser::parse_lambda_expression(ExpressionAST*& yyast) -> bool { if (lookat(TokenKind::T_LBRACKET, TokenKind::T_LBRACKET)) return false; if (lookat(TokenKind::T_LBRACKET, TokenKind::T_COLON)) return false; - if (!lookat(TokenKind::T_LBRACKET)) return false; auto _ = ScopeGuard{this}; @@ -2205,7 +2265,8 @@ auto Parser::parse_type_requirement(RequirementAST*& yyast) -> bool { auto ast = make_node(pool_); yyast = ast; - parse_optional_nested_name_specifier(ast->nestedNameSpecifier); + parse_optional_nested_name_specifier( + ast->nestedNameSpecifier, NestedNameSpecifierContext::kNonDeclarative); SourceLocation templateLoc; const auto isTemplateIntroduced = match(TokenKind::T_TEMPLATE, templateLoc); @@ -2365,7 +2426,8 @@ auto Parser::parse_member_expression(ExpressionAST*& yyast) -> bool { ast->accessLoc = accessLoc; ast->accessOp = unit->tokenKind(accessLoc); - parse_optional_nested_name_specifier(ast->nestedNameSpecifier); + parse_optional_nested_name_specifier( + ast->nestedNameSpecifier, NestedNameSpecifierContext::kNonDeclarative); ast->isTemplateIntroduced = match(TokenKind::T_TEMPLATE, ast->templateLoc); @@ -4878,6 +4940,8 @@ auto Parser::parse_notypespec_function_definition( auto _ = ScopeGuard{this}; + auto nestedNameSpecifier = decl.getNestedNameSpecifier(); + if (auto scope = decl.getScope()) { setScope(scope); } else if (auto q = decl.getNestedNameSpecifier()) { @@ -5568,7 +5632,8 @@ auto Parser::parse_named_type_specifier(SpecifierAST*& yyast, DeclSpecs& specs) LookaheadParser lookahead{this}; NestedNameSpecifierAST* nestedNameSpecifier = nullptr; - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kNonDeclarative); SourceLocation templateLoc; const auto isTemplateIntroduced = match(TokenKind::T_TEMPLATE, templateLoc); @@ -6480,7 +6545,8 @@ auto Parser::parse_elaborated_enum_specifier(SpecifierAST*& yyast, if (!match(TokenKind::T_ENUM, enumLoc)) return false; NestedNameSpecifierAST* nestedNameSpecifier = nullptr; - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kDeclarative); NameIdAST* name = nullptr; if (!parse_name_id(name)) { @@ -6512,7 +6578,8 @@ auto Parser::parse_elaborated_type_specifier( parse_optional_attribute_specifier_seq(attributes); NestedNameSpecifierAST* nestedNameSpecifier = nullptr; - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kDeclarative); SourceLocation templateLoc; const auto isTemplateIntroduced = match(TokenKind::T_TEMPLATE, templateLoc); @@ -6992,7 +7059,9 @@ auto Parser::parse_ptr_operator(PtrOperatorAST*& yyast) -> bool { LookaheadParser lookahead{this}; NestedNameSpecifierAST* nestedNameSpecifier = nullptr; - if (!parse_nested_name_specifier(nestedNameSpecifier)) return false; + if (!parse_nested_name_specifier(nestedNameSpecifier, + NestedNameSpecifierContext::kNonDeclarative)) + return false; SourceLocation starLoc; if (!match(TokenKind::T_STAR, starLoc)) return false; @@ -7071,7 +7140,8 @@ auto Parser::parse_declarator_id(CoreDeclaratorAST*& yyast, Decl& decl, if (declaratorKind != DeclaratorKind::kDeclarator) return false; NestedNameSpecifierAST* nestedNameSpecifier = nullptr; - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kDeclarative); SourceLocation templateLoc; const auto isTemplateIntroduced = match(TokenKind::T_TEMPLATE, templateLoc); @@ -7693,7 +7763,8 @@ auto Parser::parse_enum_specifier(SpecifierAST*& yyast, DeclSpecs& specs) auto Parser::parse_enum_head_name(NestedNameSpecifierAST*& nestedNameSpecifier, NameIdAST*& name) -> bool { - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kDeclarative); SourceLocation identifierLoc; @@ -8018,7 +8089,8 @@ auto Parser::parse_namespace_alias_definition(DeclarationAST*& yyast) -> bool { auto Parser::parse_qualified_namespace_specifier( NestedNameSpecifierAST*& nestedNameSpecifier, NameIdAST*& name) -> bool { - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kNonDeclarative); SourceLocation identifierLoc; @@ -8060,7 +8132,8 @@ auto Parser::parse_using_directive(DeclarationAST*& yyast) -> bool { ast->usingLoc = usingLoc; ast->namespaceLoc = namespaceLoc; - parse_optional_nested_name_specifier(ast->nestedNameSpecifier); + parse_optional_nested_name_specifier( + ast->nestedNameSpecifier, NestedNameSpecifierContext::kDeclarative); auto currentNamespace = scope_->owner(); @@ -8137,7 +8210,8 @@ auto Parser::parse_using_declarator(UsingDeclaratorAST*& yyast) -> bool { NestedNameSpecifierAST* nestedNameSpecifier = nullptr; - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kNonDeclarative); UnqualifiedIdAST* unqualifiedId = nullptr; @@ -8990,7 +9064,8 @@ auto Parser::parse_class_specifier( parse_optional_attribute_specifier_seq(attributeList); - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kDeclarative); if (lookat(TokenKind::T_IDENTIFIER)) { check_type_traits(); @@ -9774,7 +9849,8 @@ auto Parser::parse_class_or_decltype( NestedNameSpecifierAST*& yynestedNameSpecifier, SourceLocation& yytemplateLoc, UnqualifiedIdAST*& yyast) -> bool { NestedNameSpecifierAST* nestedNameSpecifier = nullptr; - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kNonDeclarative); if (!nestedNameSpecifier) { DecltypeSpecifierAST* decltypeSpecifier = nullptr; @@ -10460,7 +10536,8 @@ auto Parser::parse_type_constraint(TypeConstraintAST*& yyast, auto lookat_type_constraint = [&] { LookaheadParser lookahead{this}; - parse_optional_nested_name_specifier(nestedNameSpecifier); + parse_optional_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kNonDeclarative); if (!match(TokenKind::T_IDENTIFIER, identifierLoc)) return false; @@ -10872,7 +10949,9 @@ auto Parser::parse_typename_specifier(SpecifierAST*& yyast, DeclSpecs& specs) LookaheadParser lookahead{this}; if (!match(TokenKind::T_TYPENAME, typenameLoc)) return false; - if (!parse_nested_name_specifier(nestedNameSpecifier)) return false; + if (!parse_nested_name_specifier( + nestedNameSpecifier, NestedNameSpecifierContext::kNonDeclarative)) + return false; isTemplateIntroduced = match(TokenKind::T_TEMPLATE, templateLoc); @@ -11199,8 +11278,24 @@ auto Parser::convertName(UnqualifiedIdAST* id) -> const Name* { if (!id) return nullptr; return visit(ConvertToName{control_}, id); } + auto Parser::getFunction(Scope* scope, const Name* name, const Type* type) -> FunctionSymbol* { + auto parentScope = scope; + + while (parentScope && parentScope->isTransparent()) { + parentScope = parentScope->parent(); + } + + if (auto parentClass = symbol_cast(parentScope->owner()); + parentClass && parentClass->name() == name) { + for (auto ctor : parentClass->constructors()) { + if (control_->is_same(ctor->type(), type)) { + return ctor; + } + } + } + for (auto candidate : scope->find(name)) { if (auto function = symbol_cast(candidate)) { if (control_->is_same(function->type(), type)) { diff --git a/src/parser/cxx/parser.h b/src/parser/cxx/parser.h index cf8eca09..a31af173 100644 --- a/src/parser/cxx/parser.h +++ b/src/parser/cxx/parser.h @@ -88,6 +88,11 @@ class Parser final { kCondition, }; + enum struct NestedNameSpecifierContext { + kNonDeclarative, + kDeclarative, + }; + enum struct Prec { kLogicalOr, kLogicalAnd, @@ -181,8 +186,17 @@ class Parser final { [[nodiscard]] auto parse_unqualified_id( UnqualifiedIdAST*& yyast, NestedNameSpecifierAST* nestedNameSpecifier, bool isTemplateIntroduced, bool inRequiresClause) -> bool; - void parse_optional_nested_name_specifier(NestedNameSpecifierAST*& yyast); - [[nodiscard]] auto parse_nested_name_specifier(NestedNameSpecifierAST*& yyast) + void parse_optional_nested_name_specifier(NestedNameSpecifierAST*& yyast, + NestedNameSpecifierContext ctx); + [[nodiscard]] auto parse_nested_name_specifier(NestedNameSpecifierAST*& yyast, + NestedNameSpecifierContext ctx) + -> bool; + [[nodiscard]] auto parse_decltype_nested_name_specifier( + NestedNameSpecifierAST*& yyast, NestedNameSpecifierContext ctx) -> bool; + [[nodiscard]] auto parse_type_nested_name_specifier( + NestedNameSpecifierAST*& yyast, NestedNameSpecifierContext ctx) -> bool; + [[nodiscard]] auto parse_template_nested_name_specifier( + NestedNameSpecifierAST*& yyast, NestedNameSpecifierContext ctx, int depth) -> bool; [[nodiscard]] auto parse_lambda_expression(ExpressionAST*& yyast) -> bool; [[nodiscard]] auto parse_lambda_specifier_seq( diff --git a/tests/unit_tests/sema/template_alias_01.cc b/tests/unit_tests/sema/template_alias_01.cc index 45e53900..c7187a53 100644 --- a/tests/unit_tests/sema/template_alias_01.cc +++ b/tests/unit_tests/sema/template_alias_01.cc @@ -1,4 +1,4 @@ -// RUN: %cxx -fcheck -dump-symbols %s +// RUN: %cxx -fcheck -ftemplates -dump-symbols %s | %filecheck %s template using Pointer = T*; @@ -6,7 +6,7 @@ using Pointer = T*; using IntPointer = Pointer; // clang-format off -// CHECK: namespace -// CHECK: template typealias T* Pointer -// CHECK: parameter typename<0, 0> T -// CHECK: typealias T* IntPointer \ No newline at end of file +// CHECK:namespace +// CHECK-NEXT:template typealias T* Pointer +// CHECK-NEXT: parameter typename<0, 0> T +// CHECK-NEXT: typealias int* IntPointer diff --git a/tests/unit_tests/sema/template_class_03.cc b/tests/unit_tests/sema/template_class_03.cc new file mode 100644 index 00000000..0b6a24fe --- /dev/null +++ b/tests/unit_tests/sema/template_class_03.cc @@ -0,0 +1,41 @@ +// RUN: %cxx -ftemplates -fcheck -dump-symbols %s | %filecheck %s + +template +struct X { + X(); + X(int); + + auto self() const -> const X*; + auto self() -> X*; +}; + +template +X::X() {} + +template +X::X(int) {} + +template +auto X::self() const -> const X* { + return this; +} + +template +auto X::self() -> X* { + return this; +} + +// clang-format off +// CHECK:namespace +// CHECK-NEXT: template class X +// CHECK-NEXT: parameter typename<0, 0> T +// CHECK-NEXT: constructor X() +// CHECK-NEXT: block +// CHECK-NEXT: constructor X(int) +// CHECK-NEXT: parameters +// CHECK-NEXT: parameter int +// CHECK-NEXT: block +// CHECK-NEXT: function const ::X* self() const +// CHECK-NEXT: block +// CHECK-NEXT: function ::X* self() +// CHECK-NEXT: block \ No newline at end of file