diff --git a/src/mlir/cxx/mlir/builtins_codegen-priv.h b/src/mlir/cxx/mlir/builtins_codegen-priv.h index 8492924f..a58f1984 100644 --- a/src/mlir/cxx/mlir/builtins_codegen-priv.h +++ b/src/mlir/cxx/mlir/builtins_codegen-priv.h @@ -19,7 +19,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. - auto cxx::Codegen::ExpressionVisitor::codegenBuiltinDispatch( cxx::CallExpressionAST* ast, cxx::BuiltinFunctionKind kind) -> std::optional { diff --git a/src/mlir/cxx/mlir/codegen.cc b/src/mlir/cxx/mlir/codegen.cc index a4a0fb42..bc0f147b 100644 --- a/src/mlir/cxx/mlir/codegen.cc +++ b/src/mlir/cxx/mlir/codegen.cc @@ -935,7 +935,7 @@ auto Codegen::findOrCreateFunction(FunctionSymbol* functionSymbol) } void Codegen::enqueueFunctionBody(FunctionSymbol* symbol) { - auto target = symbol->canonical(); + auto target = symbol->isSpecialization() ? symbol : symbol->canonical(); if (auto def = target->definition()) target = def; if (!target->declaration()) return; if (!enqueuedFunctions_.insert(target).second) return; diff --git a/src/parser/cxx/ast_rewriter.h b/src/parser/cxx/ast_rewriter.h index fabf8c1b..385cff0f 100644 --- a/src/parser/cxx/ast_rewriter.h +++ b/src/parser/cxx/ast_rewriter.h @@ -22,9 +22,12 @@ #include #include +#include +#include #include #include +#include #include #include @@ -52,6 +55,11 @@ class [[nodiscard]] ASTRewriter { static auto ensureCompleteClass(TranslationUnit* unit, ClassSymbol* classSymbol) -> bool; + static void reportPendingInstantiationErrors(TranslationUnit* unit, + Symbol* primaryTemplate, + Symbol* instantiated, + SourceLocation instantiationLoc); + static auto substituteDefaultTypeId( TranslationUnit* unit, TypeIdAST* typeId, const std::vector& templateArguments, int depth, @@ -99,6 +107,10 @@ class [[nodiscard]] ASTRewriter { auto arena() const -> Arena*; auto binder() -> Binder& { return binder_; } + auto takeBodyErrors() -> std::vector { + return std::move(bodyErrors_); + } + auto restrictedToDeclarations() const -> bool; void setRestrictedToDeclarations(bool restrictedToDeclarations); @@ -192,6 +204,9 @@ class [[nodiscard]] ASTRewriter { private: auto rewriter() -> ASTRewriter* { return this; } + auto shouldCaptureBodyErrors() const -> bool; + void typeCheckAndCapture(std::function checkFn); + auto getParameterPack(ExpressionAST* ast) -> ParameterPackSymbol*; auto getTypeParameterPack(SpecifierAST* ast) -> ParameterPackSymbol*; @@ -206,6 +221,7 @@ class [[nodiscard]] ASTRewriter { TranslationUnit* unit_ = nullptr; std::vector templateArguments_; + std::vector bodyErrors_; ParameterPackSymbol* parameterPack_ = nullptr; std::optional elementIndex_; Binder binder_; diff --git a/src/parser/cxx/ast_rewriter_declarations.cc b/src/parser/cxx/ast_rewriter_declarations.cc index e4f86328..fae17428 100644 --- a/src/parser/cxx/ast_rewriter_declarations.cc +++ b/src/parser/cxx/ast_rewriter_declarations.cc @@ -28,6 +28,7 @@ #include #include #include +#include namespace cxx { @@ -427,6 +428,13 @@ auto ASTRewriter::DeclarationVisitor::operator()( copy->rparenLoc = ast->rparenLoc; copy->semicolonLoc = ast->semicolonLoc; + if (symbol_cast(binder()->instantiatingSymbol())) { + auto typeChecker = TypeChecker{translationUnit()}; + typeChecker.setScope(binder()->scope()); + typeChecker.setReportErrors(rewrite.shouldCaptureBodyErrors()); + rewrite.typeCheckAndCapture([&] { typeChecker.check(copy); }); + } + return copy; } diff --git a/src/parser/cxx/ast_rewriter_expressions.cc b/src/parser/cxx/ast_rewriter_expressions.cc index b7e4b2d1..32c59568 100644 --- a/src/parser/cxx/ast_rewriter_expressions.cc +++ b/src/parser/cxx/ast_rewriter_expressions.cc @@ -28,7 +28,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -596,6 +598,10 @@ auto ASTRewriter::ExpressionVisitor::operator()(IdExpressionAST* ast) } copy->symbol = *symbolPtr; + if (auto var = symbol_cast(*symbolPtr)) { + if (var->type()) copy->type = var->type(); + if (var->initializer()) return rewrite.expression(var->initializer()); + } } else if (copy->nestedNameSpecifier && copy->nestedNameSpecifier->symbol) { binder()->qualifiedLookupIdExpression(copy); @@ -1205,13 +1211,14 @@ auto ASTRewriter::ExpressionVisitor::operator()(MemberExpressionAST* ast) copy->symbol = symbol; copy->type = symbol->type(); - // Propagate value category from the base expression. if (auto field = symbol_cast(symbol); field && !field->isStatic()) { copy->valueCategory = ast->valueCategory; } } } + } else if (!isDependent(translationUnit(), objectType)) { + copy->type = nullptr; } } } @@ -1428,6 +1435,10 @@ auto ASTRewriter::ExpressionVisitor::operator()(SizeofExpressionAST* ast) copy->sizeofLoc = ast->sizeofLoc; copy->expression = rewrite.expression(ast->expression); + if (copy->expression && copy->expression->type) { + copy->value = control()->memoryLayout()->sizeOf(copy->expression->type); + } + return copy; } @@ -1442,6 +1453,10 @@ auto ASTRewriter::ExpressionVisitor::operator()(SizeofTypeExpressionAST* ast) copy->typeId = rewrite.typeId(ast->typeId); copy->rparenLoc = ast->rparenLoc; + if (copy->typeId && copy->typeId->type) { + copy->value = control()->memoryLayout()->sizeOf(copy->typeId->type); + } + return copy; } diff --git a/src/parser/cxx/ast_rewriter_instantiate.cc b/src/parser/cxx/ast_rewriter_instantiate.cc index 674675fa..bcf49ccf 100644 --- a/src/parser/cxx/ast_rewriter_instantiate.cc +++ b/src/parser/cxx/ast_rewriter_instantiate.cc @@ -202,8 +202,8 @@ struct Instantiate { auto operator()(Symbol*) -> Symbol* { return nullptr; } }; -auto isPrimaryTemplate(const std::vector& templateArguments) - -> bool { +[[nodiscard]] auto isPrimaryTemplate( + const std::vector& templateArguments) -> bool { int expected = 0; for (const auto& arg : templateArguments) { if (!std::holds_alternative(arg)) return false; @@ -238,7 +238,8 @@ auto isPrimaryTemplate(const std::vector& templateArguments) return true; } -auto templateParameterCount(TemplateDeclarationAST* templateDecl) -> int { +[[nodiscard]] auto templateParameterCount(TemplateDeclarationAST* templateDecl) + -> int { if (!templateDecl) return 0; int count = 0; for (auto parameter : ListView{templateDecl->templateParameterList}) { @@ -248,7 +249,7 @@ auto templateParameterCount(TemplateDeclarationAST* templateDecl) -> int { return count; } -auto computeInstantiationClassName( +[[nodiscard]] auto computeInstantiationClassName( TranslationUnit* unit, Symbol* primaryTemplate, const std::vector& templateArguments) -> std::string { if (!primaryTemplate) return "template"; @@ -256,20 +257,13 @@ auto computeInstantiationClassName( templateArguments)); } -struct CapturingDiagnosticsClient final : DiagnosticsClient { - DiagnosticsClient* parent = nullptr; - std::vector diagnostics; - - explicit CapturingDiagnosticsClient(DiagnosticsClient* parent) - : parent(parent) {} - - void report(const Diagnostic& diagnostic) override { - diagnostics.push_back(diagnostic); - if (parent) parent->report(diagnostic); - } -}; +[[nodiscard]] auto instantiationLabel(Symbol* symbol) -> std::string_view { + return symbol_cast(symbol) + ? "function template specialization" + : "template class"; +} -auto findMutableSpecialization(Symbol* primary, Symbol* spec) +[[nodiscard]] auto findMutableSpecialization(Symbol* primary, Symbol* spec) -> TemplateSpecialization* { if (!primary || !spec) return nullptr; auto search = [spec](auto sym) -> TemplateSpecialization* { @@ -309,6 +303,25 @@ auto ASTRewriter::substituteDefaultTypeId( return rewriter.typeId(typeId); } +void ASTRewriter::reportPendingInstantiationErrors( + TranslationUnit* unit, Symbol* primaryTemplate, Symbol* instantiated, + SourceLocation instantiationLoc) { + if (!primaryTemplate || !instantiated || !instantiationLoc) return; + if (auto spec = findMutableSpecialization(primaryTemplate, instantiated)) { + if (!spec->instantiationErrors.empty()) { + for (auto& diag : spec->instantiationErrors) + unit->diagnosticsClient()->report(diag); + spec->instantiationErrors.clear(); + auto name = + computeInstantiationClassName(unit, primaryTemplate, spec->arguments); + auto label = instantiationLabel(primaryTemplate); + unit->note(instantiationLoc, + std::format("in instantiation of {} '{}' requested here", + label, name)); + } + } +} + auto ASTRewriter::instantiate(TranslationUnit* unit, List* templateArgumentList, Symbol* symbol, SourceLocation instantiationLoc, @@ -354,24 +367,16 @@ auto ASTRewriter::instantiate(TranslationUnit* unit, if (auto cached = visit(GetSpecialization{templateArguments}, symbol)) { auto cachedClass = symbol_cast(cached); if (!cachedClass) { + if (!sfinaeContext) + reportPendingInstantiationErrors(unit, symbol, cached, + instantiationLoc); if (savedDiagClient) (void)unit->changeDiagnosticsClient(savedDiagClient); return cached; } if (cachedClass->declaration()) { - if (!sfinaeContext && instantiationLoc) { - if (auto spec = findMutableSpecialization(symbol, cached)) { - if (!spec->instantiationErrors.empty()) { - for (auto& diag : spec->instantiationErrors) - unit->diagnosticsClient()->report(diag); - auto className = - computeInstantiationClassName(unit, symbol, templateArguments); - unit->note(instantiationLoc, - std::format("in instantiation of template class '{}' " - "requested here", - className)); - } - } - } + if (!sfinaeContext) + reportPendingInstantiationErrors(unit, symbol, cached, + instantiationLoc); if (savedDiagClient) (void)unit->changeDiagnosticsClient(savedDiagClient); return cached; } @@ -419,6 +424,12 @@ auto ASTRewriter::instantiate(TranslationUnit* unit, auto instance = visit(Instantiate{rewriter}, symbol); (void)unit->changeDiagnosticsClient(savedDiagClient); if (sfinaeClient->hadError) return nullptr; + auto bodyErrors = rewriter.takeBodyErrors(); + if (!bodyErrors.empty() && instance) { + if (auto spec = findMutableSpecialization(symbol, instance)) { + spec->instantiationErrors = std::move(bodyErrors); + } + } return instance; } @@ -429,17 +440,22 @@ auto ASTRewriter::instantiate(TranslationUnit* unit, (void)unit->changeDiagnosticsClient(capturing.parent); + auto bodyErrors = rewriter.takeBodyErrors(); + capturing.diagnostics.insert(capturing.diagnostics.end(), + std::make_move_iterator(bodyErrors.begin()), + std::make_move_iterator(bodyErrors.end())); + if (!capturing.diagnostics.empty()) { if (auto spec = findMutableSpecialization(symbol, instantiatedSymbol)) { - spec->instantiationErrors = capturing.diagnostics; + spec->instantiationErrors = std::move(capturing.diagnostics); } if (instantiationLoc) { - auto className = + auto name = computeInstantiationClassName(unit, symbol, templateArguments); + auto label = instantiationLabel(symbol); unit->note(instantiationLoc, - std::format("in instantiation of template class '{}' " - "requested here", - className)); + std::format("in instantiation of {} '{}' requested here", + label, name)); } } diff --git a/src/parser/cxx/ast_rewriter_requires.cc b/src/parser/cxx/ast_rewriter_requires.cc index 1c85d5ad..5f13ddf5 100644 --- a/src/parser/cxx/ast_rewriter_requires.cc +++ b/src/parser/cxx/ast_rewriter_requires.cc @@ -23,12 +23,32 @@ // cxx #include #include +#include #include #include #include namespace cxx { +auto ASTRewriter::shouldCaptureBodyErrors() const -> bool { + return symbol_cast(binder_.instantiatingSymbol()) && + binder_.reportErrors(); +} + +void ASTRewriter::typeCheckAndCapture(std::function checkFn) { + if (shouldCaptureBodyErrors()) { + CapturingDiagnosticsClient capture; + auto saved = unit_->changeDiagnosticsClient(&capture); + checkFn(); + (void)unit_->changeDiagnosticsClient(saved); + bodyErrors_.insert(bodyErrors_.end(), + std::make_move_iterator(capture.diagnostics.begin()), + std::make_move_iterator(capture.diagnostics.end())); + } else { + checkFn(); + } +} + auto ASTRewriter::checkRequiresClause( TranslationUnit* unit, Symbol* symbol, RequiresClauseAST* clause, const std::vector& templateArguments, int depth) -> bool { @@ -52,9 +72,13 @@ auto ASTRewriter::checkRequiresClause( } void ASTRewriter::check(ExpressionAST* ast) { + if (!ast) return; + if (isDependent(unit_, ast)) return; + auto typeChecker = TypeChecker{unit_}; typeChecker.setScope(binder_.scope()); - typeChecker.check(ast); + typeChecker.setReportErrors(shouldCaptureBodyErrors()); + typeCheckAndCapture([&] { typeChecker.check(ast); }); } } // namespace cxx diff --git a/src/parser/cxx/bind_class.cc b/src/parser/cxx/bind_class.cc index 9a430663..036200b5 100644 --- a/src/parser/cxx/bind_class.cc +++ b/src/parser/cxx/bind_class.cc @@ -78,9 +78,7 @@ auto templateArgListsEquivalent(List* a, auto exprB = ast_cast(itB->value); if (exprA && exprB) { if (exprA->expression == exprB->expression) continue; - if (!exprA->expression || !exprB->expression) return false; - if (exprA->expression->type != exprB->expression->type) return false; - continue; + return false; } return false; @@ -163,6 +161,47 @@ void Binder::BindClass::initializeClassSymbol(ClassSymbol* classSymbol) { ast->symbol->setFinal(ast->isFinal); if (declSpecs.templateHead) { + if (auto oldDecl = ast->symbol->templateDeclaration()) { + auto mergeDefault = [](TemplateParameterAST* src, + TemplateParameterAST* dst) { + if (auto s = ast_cast(src)) { + auto d = ast_cast(dst); + if (d && s->typeId && !d->typeId) { + d->equalLoc = s->equalLoc; + d->typeId = s->typeId; + } + } else if (auto s = ast_cast(src)) { + auto d = ast_cast(dst); + if (d && s->declaration && d->declaration && + s->declaration->expression && !d->declaration->expression) { + d->declaration->equalLoc = s->declaration->equalLoc; + d->declaration->expression = s->declaration->expression; + } + } else if (auto s = ast_cast(src)) { + auto d = ast_cast(dst); + if (d && s->idExpression && !d->idExpression) { + d->equalLoc = s->equalLoc; + d->idExpression = s->idExpression; + } + } else if (auto s = ast_cast(src)) { + auto d = ast_cast(dst); + if (d && s->typeId && !d->typeId) { + d->equalLoc = s->equalLoc; + d->typeId = s->typeId; + } + } + }; + + auto oldParams = ListView{oldDecl->templateParameterList}; + auto newParams = ListView{declSpecs.templateHead->templateParameterList}; + auto newIt = newParams.begin(); + for (auto oldIt = oldParams.begin(); + oldIt != oldParams.end() && newIt != newParams.end(); + ++oldIt, ++newIt) { + mergeDefault(*oldIt, *newIt); + } + } + ast->symbol->setTemplateDeclaration(declSpecs.templateHead); ast->symbol->setTemplateParameters(declSpecs.templateHead->symbol); } diff --git a/src/parser/cxx/binder.cc b/src/parser/cxx/binder.cc index 5c97e29d..fc52a8fe 100644 --- a/src/parser/cxx/binder.cc +++ b/src/parser/cxx/binder.cc @@ -151,25 +151,7 @@ auto Binder::scope() const -> ScopeSymbol* { return scope_; } void Binder::setScope(ScopeSymbol* scope) { scope_ = scope; - inTemplate_ = false; - - for (auto current = scope_; current; current = current->parent()) { - if (current->isTemplateParameters()) { - inTemplate_ = true; - break; - } - if (auto cls = symbol_cast(current)) { - if (cls->templateParameters()) { - inTemplate_ = true; - break; - } - } else if (auto func = symbol_cast(current)) { - if (func->templateParameters()) { - inTemplate_ = true; - break; - } - } - } + inTemplate_ = isEnclosedInTemplate(scope_); } auto Binder::languageLinkage() const -> LanguageKind { @@ -876,16 +858,14 @@ void Binder::complete(LambdaExpressionAST* ast) { returnType, std::move(parameterTypes), isVariadic, {}, {}, isNoexcept); ast->symbol->setType(funcType); - if (isCxx() && !inTemplate()) { + if (isCxx() && !isEnclosedInTemplate(ast->symbol->parent())) { auto closureName = control()->getIdentifier(std::format("__lambda_{}", lambdaCount_++)); - // Create the ClassSymbol for the closure type auto classSymbol = control()->newClassSymbol(parentScope, ast->lbracketLoc); classSymbol->setName(closureName); parentScope->addSymbol(classSymbol); - // Create operator() FunctionSymbol auto operatorCallName = control()->getOperatorId(TokenKind::T_LPAREN); auto operatorFunc = control()->newFunctionSymbol(classSymbol, ast->lbracketLoc); @@ -901,7 +881,23 @@ void Binder::complete(LambdaExpressionAST* ast) { } } - // Create implicit default constructor + if (ast->symbol->isTemplate()) { + auto ar = unit_->arena(); + auto templateParamsSymbol = control()->newTemplateParametersSymbol( + operatorFunc, ast->lbracketLoc); + for (auto p : ListView{ast->templateParameterList}) { + if (p && p->symbol) templateParamsSymbol->addSymbol(p->symbol); + } + int depth = ast->templateParameterList + ? ast->templateParameterList->value->depth + : 0; + auto templateDecl = TemplateDeclarationAST::create( + ar, ast->templateParameterList, ast->templateRequiresClause, + /*declaration=*/nullptr, templateParamsSymbol, depth); + operatorFunc->setTemplateParameters(templateParamsSymbol); + operatorFunc->setTemplateDeclaration(templateDecl); + } + auto ctorSymbol = control()->newFunctionSymbol(classSymbol, ast->lbracketLoc); ctorSymbol->setName(closureName); @@ -912,7 +908,8 @@ void Binder::complete(LambdaExpressionAST* ast) { ctorSymbol->setLanguageLinkage(LanguageKind::kCXX); classSymbol->addConstructor(ctorSymbol); - if (ast->captureDefault == TokenKind::T_EOF_SYMBOL && !ast->captureList) { + if (!ast->symbol->isTemplate() && + ast->captureDefault == TokenKind::T_EOF_SYMBOL && !ast->captureList) { auto fptrType = control()->getPointerType(funcType); auto convFuncType = control()->getFunctionType(fptrType, {}); auto convName = control()->getConversionFunctionId(fptrType); @@ -974,6 +971,9 @@ void Binder::completeLambdaBody(LambdaExpressionAST* ast) { funcChunk->parameterDeclarationClause = ast->parameterDeclarationClause->clone(ar); } + if (ast->trailingReturnType) { + funcChunk->trailingReturnType = ast->trailingReturnType->clone(ar); + } auto declarator = DeclaratorAST::create( ar, /*ptrOpList=*/nullptr, /*coreDeclarator=*/idDecl, @@ -987,8 +987,17 @@ void Binder::completeLambdaBody(LambdaExpressionAST* ast) { funcDef->declarator = declarator; funcDef->functionBody = funcBody; funcDef->symbol = operatorFunc; + + if (!ast->trailingReturnType) { + auto autoSpec = AutoTypeSpecifierAST::create(ar); + funcDef->declSpecifierList = make_list_node(ar, autoSpec); + } + operatorFunc->setDeclaration(funcDef); + if (auto templateDecl = operatorFunc->templateDeclaration()) + templateDecl->declaration = funcDef; + // Build FunctionDefinitionAST for the default constructor auto closureName = name_cast(classSymbol->name()); for (auto ctor : classSymbol->constructors()) { diff --git a/src/parser/cxx/dependent_types.cc b/src/parser/cxx/dependent_types.cc index ecb910a8..2cf82c77 100644 --- a/src/parser/cxx/dependent_types.cc +++ b/src/parser/cxx/dependent_types.cc @@ -49,15 +49,7 @@ struct IsDependent { [[nodiscard]] auto isInTemplateScope(Symbol* symbol) -> bool { if (auto var = symbol_cast(symbol)) if (var->templateParameters()) return true; - for (auto scope = symbol->parent(); scope; scope = scope->parent()) { - if (scope->isTemplateParameters()) return true; - if (auto cls = symbol_cast(scope)) { - if (cls->templateParameters()) return true; - } else if (auto func = symbol_cast(scope)) { - if (func->templateParameters()) return true; - } - } - return false; + return isEnclosedInTemplate(symbol->parent()); } [[nodiscard]] auto isDependent(const Type* type) -> bool { @@ -502,6 +494,8 @@ auto IsDependent::operator()(IdExpressionAST* ast) -> bool { } auto IsDependent::operator()(LambdaExpressionAST* ast) -> bool { + if (ast->symbol && ast->symbol->isTemplate()) return true; + for (auto node : ListView{ast->captureList}) { if (isDependent(node)) return true; } diff --git a/src/parser/cxx/diagnostics_client.h b/src/parser/cxx/diagnostics_client.h index 35e101fa..e72d28ab 100644 --- a/src/parser/cxx/diagnostics_client.h +++ b/src/parser/cxx/diagnostics_client.h @@ -22,6 +22,8 @@ #include +#include + namespace cxx { class Preprocessor; @@ -87,4 +89,17 @@ class DiagnosticsClient { bool fatalErrors_ = false; }; +struct CapturingDiagnosticsClient final : DiagnosticsClient { + DiagnosticsClient* parent = nullptr; + std::vector diagnostics; + + explicit CapturingDiagnosticsClient(DiagnosticsClient* parent = nullptr) + : parent(parent) {} + + void report(const Diagnostic& diagnostic) override { + diagnostics.push_back(diagnostic); + if (parent) parent->report(diagnostic); + } +}; + } // namespace cxx diff --git a/src/parser/cxx/implicit_conversion_sequence.h b/src/parser/cxx/implicit_conversion_sequence.h index d3280591..8620d5d1 100644 --- a/src/parser/cxx/implicit_conversion_sequence.h +++ b/src/parser/cxx/implicit_conversion_sequence.h @@ -58,6 +58,7 @@ struct ImplicitConversionSequence { bool bindsToRvalueRef = false; bool bindsToReference = false; + bool hasQualificationConversion = false; CvQualifiers referenceCv = CvQualifiers::kNone; [[nodiscard]] auto isBetterThan(const ImplicitConversionSequence& other) const @@ -80,6 +81,10 @@ struct ImplicitConversionSequence { return referenceCv < other.referenceCv; } + if (hasQualificationConversion != other.hasQualificationConversion) { + return !hasQualificationConversion; + } + return false; } diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 84298418..a064a3b3 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -1324,6 +1324,10 @@ auto Parser::parse_lambda_expression(ExpressionAST*& yyast) -> bool { expect(TokenKind::T_GREATER, ast->greaterLoc); + ast->symbol->setTemplate(true); + + pushScope(ast->symbol); + (void)parse_requires_clause(ast->templateRequiresClause); } @@ -1338,7 +1342,9 @@ auto Parser::parse_lambda_expression(ExpressionAST*& yyast) -> bool { expect(TokenKind::T_RPAREN, ast->rparenLoc); - setScope(ast->parameterDeclarationClause->functionParametersSymbol); + if (ast->parameterDeclarationClause) { + setScope(ast->parameterDeclarationClause->functionParametersSymbol); + } } parse_optional_attribute_specifier_seq(ast->gnuAtributeList, @@ -4435,13 +4441,14 @@ auto Parser::parse_empty_or_attribute_declaration( auto Parser::parse_notypespec_function_definition( DeclarationAST*& yyast, List* atributes, - BindingContext ctx) -> bool { + BindingContext ctx, TemplateDeclarationAST* templateHead) -> bool { if (!isCxx()) return false; if (!context_allows_function_definition(ctx)) return false; LookaheadParser lookahead{this}; DeclSpecs specs{unit_}; + specs.templateHead = templateHead; List* declSpecifierList = nullptr; auto parse_optional_decl_specifier_seq_no_typespecs = [&] { @@ -4570,7 +4577,9 @@ auto Parser::parse_simple_declaration(DeclarationAST*& yyast, if (parse_empty_or_attribute_declaration(yyast, attributes, ctx)) return true; - if (parse_notypespec_function_definition(yyast, attributes, ctx)) return true; + if (parse_notypespec_function_definition(yyast, attributes, ctx, + templateHead)) + return true; DeclSpecs specs{unit_}; specs.templateHead = templateHead; @@ -4812,6 +4821,12 @@ auto Parser::parse_notypespec_function_definition( auto functionSymbol = binder_.declareFunction(declarator, decl); + if (auto templateHead = specs.templateHead) { + functionSymbol->setTemplateDeclaration(templateHead); + functionSymbol->setTemplateParameters(templateHead->symbol); + mark_maybe_template_name(declarator); + } + SourceLocation semicolonLoc; if (isPure) { diff --git a/src/parser/cxx/parser.h b/src/parser/cxx/parser.h index 435b66b6..c6ee825f 100644 --- a/src/parser/cxx/parser.h +++ b/src/parser/cxx/parser.h @@ -454,7 +454,8 @@ class Parser final { [[nodiscard]] auto parse_notypespec_function_definition( DeclarationAST*& yyast, List* atributes, - BindingContext ctx) -> bool; + BindingContext ctx, TemplateDeclarationAST* templateHead = nullptr) + -> bool; [[nodiscard]] auto parse_notypespec_function_definition( DeclarationAST*& yyast, List* declSpecifierList, diff --git a/src/parser/cxx/standard_conversion.cc b/src/parser/cxx/standard_conversion.cc index 34d3b3a8..b7b8cb38 100644 --- a/src/parser/cxx/standard_conversion.cc +++ b/src/parser/cxx/standard_conversion.cc @@ -884,6 +884,7 @@ auto StandardConversion::computeConversionSequence(ExpressionAST* expr, if (unit_->typeTraits().is_same(fromUnqual, toUnqual)) { seq.rank = ConversionRank::kExactMatch; + seq.hasQualificationConversion = true; addStep(ImplicitCastKind::kQualificationConversion, targetType); return seq; } diff --git a/src/parser/cxx/substitution.cc b/src/parser/cxx/substitution.cc index 543bf96d..958df2d2 100644 --- a/src/parser/cxx/substitution.cc +++ b/src/parser/cxx/substitution.cc @@ -76,15 +76,7 @@ struct Substitution::CollectRawTemplateArgument { Substitution& subst; [[nodiscard]] auto isInTemplateScope(Symbol* symbol) -> bool { - for (auto scope = symbol->parent(); scope; scope = scope->parent()) { - if (scope->isTemplateParameters()) return true; - if (auto cls = symbol_cast(scope)) { - if (cls->templateParameters()) return true; - } else if (auto func = symbol_cast(scope)) { - if (func->templateParameters()) return true; - } - } - return false; + return isEnclosedInTemplate(symbol->parent()); } auto operator()(ExpressionTemplateArgumentAST* ast) -> std::optional; @@ -112,10 +104,13 @@ struct Substitution::MakeDefaultTemplateArgument { auto Substitution::MakeDefaultTemplateArgument::operator()( TemplateTypeParameterAST* parameter) -> std::optional { - subst.error(parameter->firstSourceLocation(), - "default template argument for template template " - "parameters is not implemented"); - return std::nullopt; + if (!parameter->idExpression || !parameter->idExpression->symbol) { + subst.maybeReportMissingTemplateArgument(parameter->firstSourceLocation()); + return std::nullopt; + } + auto argument = control()->newTypeAliasSymbol(nullptr, {}); + argument->setType(parameter->idExpression->symbol->type()); + return argument; } auto Substitution::MakeDefaultTemplateArgument::operator()( diff --git a/src/parser/cxx/symbols.cc b/src/parser/cxx/symbols.cc index 541ab4f9..2c4a479c 100644 --- a/src/parser/cxx/symbols.cc +++ b/src/parser/cxx/symbols.cc @@ -967,6 +967,10 @@ auto LambdaSymbol::isStatic() const -> bool { return isStatic_; } void LambdaSymbol::setStatic(bool isStatic) { isStatic_ = isStatic; } +auto LambdaSymbol::isTemplate() const -> bool { return isTemplate_; } + +void LambdaSymbol::setTemplate(bool isTemplate) { isTemplate_ = isTemplate; } + FunctionParametersSymbol::FunctionParametersSymbol(ScopeSymbol* enclosingScope) : ScopeSymbol(Kind, enclosingScope) {} diff --git a/src/parser/cxx/symbols.h b/src/parser/cxx/symbols.h index 46b2d2a6..def717c1 100644 --- a/src/parser/cxx/symbols.h +++ b/src/parser/cxx/symbols.h @@ -781,6 +781,9 @@ class LambdaSymbol final : public ScopeSymbol { [[nodiscard]] auto isStatic() const -> bool; void setStatic(bool isStatic); + [[nodiscard]] auto isTemplate() const -> bool; + void setTemplate(bool isTemplate); + private: union { std::uint32_t flags_{}; @@ -789,6 +792,7 @@ class LambdaSymbol final : public ScopeSymbol { std::uint32_t isConsteval_ : 1; std::uint32_t isMutable_ : 1; std::uint32_t isStatic_ : 1; + std::uint32_t isTemplate_ : 1; }; }; }; @@ -1140,4 +1144,18 @@ inline auto symbol_cast(Symbol* symbol) -> ScopeSymbol* { return true; } +[[nodiscard]] inline auto isEnclosedInTemplate(ScopeSymbol* scope) -> bool { + for (; scope; scope = scope->parent()) { + if (scope->isTemplateParameters()) return true; + if (auto cls = symbol_cast(scope)) { + if (cls->templateParameters()) return true; + } else if (auto func = symbol_cast(scope)) { + if (func->templateParameters()) return true; + } else if (auto lambda = symbol_cast(scope)) { + if (lambda->isTemplate()) return true; + } + } + return false; +} + } // namespace cxx diff --git a/src/parser/cxx/template_argument_deduction.cc b/src/parser/cxx/template_argument_deduction.cc index d0154420..fa364e66 100644 --- a/src/parser/cxx/template_argument_deduction.cc +++ b/src/parser/cxx/template_argument_deduction.cc @@ -101,6 +101,7 @@ void TemplateArgumentDeduction::collectTemplateParameters( } templateParams_.push_back(info); + templateParams_.back().parameterAST = p; } auto n = templateParams_.size(); @@ -165,8 +166,7 @@ auto TemplateArgumentDeduction::isForwardingReference(const Type* paramType) auto rrefParam = type_cast(paramType); if (!rrefParam) return false; - auto rrefElem = unit_->typeTraits().remove_cv(rrefParam->elementType()); - auto paramTpt = type_cast(rrefElem); + auto paramTpt = type_cast(rrefParam->elementType()); if (!paramTpt) return false; return !paramTpt->isParameterPack(); @@ -298,7 +298,11 @@ auto TemplateArgumentDeduction::deduceFromCallArgument(const Type* P, unit_->typeTraits().remove_reference(A))); } - return deduceTypeFromType(P, unit_->typeTraits().remove_cvref(A)); + if (unit_->typeTraits().is_reference(P)) + return deduceTypeFromType(P, unit_->typeTraits().remove_reference(A)); + + return deduceTypeFromType(P, unit_->typeTraits().remove_cv( + unit_->typeTraits().remove_reference(A))); } auto TemplateArgumentDeduction::deduceFromCall(const FunctionType* functionType, @@ -384,8 +388,22 @@ auto TemplateArgumentDeduction::buildTemplateArgumentList() } if (!deducedTypes_[i]) { - if (templateParams_[i].hasDefault) break; - return std::nullopt; + if (!templateParams_[i].hasDefault) return std::nullopt; + auto p = templateParams_[i].parameterAST; + if (auto n = ast_cast(p)) { + if (!n->declaration || !n->declaration->expression) return std::nullopt; + auto exprArg = ExpressionTemplateArgumentAST::create(arena_); + exprArg->expression = n->declaration->expression; + *argListIt = make_list_node(arena_, exprArg); + argListIt = &(*argListIt)->next; + } else if (auto t = ast_cast(p)) { + if (!t->typeId) return std::nullopt; + auto typeArg = TypeTemplateArgumentAST::create(arena_); + typeArg->typeId = t->typeId; + *argListIt = make_list_node(arena_, typeArg); + argListIt = &(*argListIt)->next; + } + continue; } auto typeId = TypeIdAST::create(arena_); diff --git a/src/parser/cxx/template_argument_deduction.h b/src/parser/cxx/template_argument_deduction.h index a115edfb..0fe235c9 100644 --- a/src/parser/cxx/template_argument_deduction.h +++ b/src/parser/cxx/template_argument_deduction.h @@ -43,6 +43,7 @@ struct TemplateParameterInfo { }; const TypeParameterType* typeParameterType = nullptr; + TemplateParameterAST* parameterAST = nullptr; bool isPack = false; bool hasDefault = false; Kind kind = Kind::kUnknown; diff --git a/src/parser/cxx/type_checker.cc b/src/parser/cxx/type_checker.cc index 208363dc..02295af9 100644 --- a/src/parser/cxx/type_checker.cc +++ b/src/parser/cxx/type_checker.cc @@ -218,15 +218,7 @@ struct TypeChecker::Visitor { } [[nodiscard]] auto in_template() const -> bool { - for (auto current = scope(); current; current = current->parent()) { - if (current->isTemplateParameters()) return true; - if (auto cls = symbol_cast(current)) { - if (cls->templateParameters()) return true; - } else if (auto func = symbol_cast(current)) { - if (func->templateParameters()) return true; - } - } - return false; + return isEnclosedInTemplate(scope()); } void warning(SourceLocation loc, std::string message) { @@ -1005,6 +997,22 @@ void TypeChecker::Visitor::resolve_call_overload( } } + if (!isMemberCall && !allFunctions.empty() && + !allFunctions.front()->isStatic()) { + for (auto current = check.scope_; current; current = current->parent()) { + if (auto funcSym = symbol_cast(current)) { + if (symbol_cast(funcSym->parent())) { + if (auto funcType = type_cast(funcSym->type())) { + isMemberCall = true; + objectCv = funcType->cvQualifiers(); + objectValueCategory = ValueCategory::kLValue; + } + } + break; + } + } + } + std::vector candidates; auto explicitTemplateArguments = [&]() -> List* { @@ -1035,7 +1043,8 @@ void TypeChecker::Visitor::resolve_call_overload( explicitTemplateArguments); if (!deducedArgs.has_value()) continue; auto instantiated = - ASTRewriter::instantiate(check.unit_, *deducedArgs, func, {}, + ASTRewriter::instantiate(check.unit_, *deducedArgs, func, + ast->baseExpression->firstSourceLocation(), /*sfinaeContext=*/true); if (!instantiated) continue; auto instFunc = symbol_cast(instantiated); @@ -1114,6 +1123,11 @@ void TypeChecker::Visitor::resolve_call_overload( } auto function = bestPtr->symbol; + if (function->isSpecialization()) { + ASTRewriter::reportPendingInstantiationErrors( + check.unit_, function->primaryTemplateSymbol(), function, + ast->baseExpression->firstSourceLocation()); + } ast->baseExpression->type = function->type(); set_base_symbol(ast->baseExpression, function); @@ -1189,6 +1203,8 @@ auto TypeChecker::Visitor::resolve_call_operator(CallExpressionAST* ast) std::vector viableCandidates; for (auto func : allFunctions) { + if (func->templateDeclaration() && !func->isSpecialization()) continue; + auto type = type_cast(func->type()); if (!type) continue; @@ -1245,6 +1261,39 @@ auto TypeChecker::Visitor::resolve_call_operator(CallExpressionAST* ast) if (cand.viable) viableCandidates.push_back(std::move(cand)); } + if (viableCandidates.empty()) { + bool anyDeductionSucceeded = false; + for (auto func : allFunctions) { + if (!func->templateDeclaration() || func->isSpecialization()) continue; + auto deducedArgs = + deduceTemplateArguments(func, ast->expressionList, nullptr); + if (!deducedArgs.has_value()) continue; + anyDeductionSucceeded = true; + auto instantiated = ASTRewriter::instantiate( + check.unit_, *deducedArgs, func, + ast->baseExpression->firstSourceLocation(), /*sfinaeContext=*/true); + if (!instantiated) continue; + auto instFunc = symbol_cast(instantiated); + if (!instFunc) continue; + Candidate cand{instFunc}; + cand.viable = true; + viableCandidates.push_back(std::move(cand)); + } + if (anyDeductionSucceeded && viableCandidates.empty()) { + for (auto func : allFunctions) { + if (!func->templateDeclaration() || func->isSpecialization()) continue; + auto deducedArgs = + deduceTemplateArguments(func, ast->expressionList, nullptr); + if (!deducedArgs.has_value()) continue; + (void)ASTRewriter::instantiate( + check.unit_, *deducedArgs, func, + ast->baseExpression->firstSourceLocation(), + /*sfinaeContext=*/false); + } + return nullptr; + } + } + auto [bestPtr, ambiguous] = resolution.selectBestViableFunction(viableCandidates, true, false); @@ -1256,6 +1305,23 @@ auto TypeChecker::Visitor::resolve_call_operator(CallExpressionAST* ast) } auto operatorFunc = bestPtr->symbol; + if (operatorFunc->templateDeclaration() && + !operatorFunc->isSpecialization()) { + auto deducedArgs = + deduceTemplateArguments(operatorFunc, ast->expressionList, nullptr); + if (deducedArgs.has_value()) { + auto instantiated = ASTRewriter::instantiate( + check.unit_, *deducedArgs, operatorFunc, + ast->baseExpression->firstSourceLocation(), /*sfinaeContext=*/true); + if (auto instFunc = symbol_cast(instantiated)) + operatorFunc = instFunc; + } + } + if (operatorFunc->isSpecialization()) { + ASTRewriter::reportPendingInstantiationErrors( + check.unit_, operatorFunc->primaryTemplateSymbol(), operatorFunc, + ast->baseExpression->firstSourceLocation()); + } auto functionType = type_cast(operatorFunc->type()); if (!functionType) return nullptr; @@ -1429,7 +1495,8 @@ void TypeChecker::Visitor::operator()(CallExpressionAST* ast) { } } - if (in_template() && is_dependent_type(ast->baseExpression->type)) { + if (in_template() && (is_dependent_type(ast->baseExpression->type) || + type_cast(ast->baseExpression->type))) { ast->type = dependent_type(); ast->valueCategory = ValueCategory::kPrValue; return; @@ -1581,7 +1648,8 @@ void TypeChecker::Visitor::operator()(CallExpressionAST* ast) { explicitTemplateArguments); if (!deducedArgs.has_value()) return; auto instantiated = - ASTRewriter::instantiate(check.unit_, *deducedArgs, funcSym, {}, + ASTRewriter::instantiate(check.unit_, *deducedArgs, funcSym, + ast->baseExpression->firstSourceLocation(), /*sfinaeContext=*/true); if (!instantiated) return; auto instFunc = symbol_cast(instantiated); @@ -1591,6 +1659,11 @@ void TypeChecker::Visitor::operator()(CallExpressionAST* ast) { set_base_symbol(ast->baseExpression, instFunc); ast->baseExpression->type = instFunc->type(); functionType = instType; + if (instFunc->isSpecialization()) { + ASTRewriter::reportPendingInstantiationErrors( + check.unit_, instFunc->primaryTemplateSymbol(), instFunc, + ast->baseExpression->firstSourceLocation()); + } }; if (auto idExpr = ast_cast(ast->baseExpression)) @@ -4159,6 +4232,9 @@ auto TypeChecker::Visitor::check_member_access(MemberExpressionAST* ast) auto memberName = get_name(control(), ast->unqualifiedId); + auto templateId = ast_cast(ast->unqualifiedId); + if (templateId && templateId->identifier) memberName = templateId->identifier; + auto classSymbol = classType->symbol(); check.unit_->typeTraits().requireCompleteClass(classSymbol); @@ -4178,6 +4254,8 @@ auto TypeChecker::Visitor::check_member_access(MemberExpressionAST* ast) auto member = std::string{""}; if (auto nameId = ast_cast(ast->unqualifiedId)) { if (auto identifier = nameId->identifier) member = identifier->value(); + } else if (templateId && templateId->identifier) { + member = templateId->identifier->value(); } error(ast->firstSourceLocation(), @@ -4233,7 +4311,7 @@ auto TypeChecker::Visitor::check_pseudo_destructor_access( } auto dtor = ast_cast(ast->unqualifiedId); - if (!dtor) return true; + if (!dtor) return false; auto name = ast_cast(dtor->id); if (!name) return true; diff --git a/src/parser/cxx/type_checker_initializer.cc b/src/parser/cxx/type_checker_initializer.cc index 7636408f..00a1a5e4 100644 --- a/src/parser/cxx/type_checker_initializer.cc +++ b/src/parser/cxx/type_checker_initializer.cc @@ -993,6 +993,8 @@ struct ReferenceInitChecker { void ReferenceInitChecker::check(VariableSymbol* var, InitDeclaratorAST* ast) { auto targetType = var->type(); + if (isDependent(ctx.unit, targetType)) return; + if (!ast->initializer) { auto loc = ctx.checker.getInitDeclaratorLocation(ast, var); ctx.error(loc, @@ -1026,6 +1028,8 @@ void ReferenceInitChecker::check(VariableSymbol* var, InitDeclaratorAST* ast) { auto seq = ctx.checker.checkImplicitConversion(conversionTarget, targetType); if (seq.rank == ConversionRank::kNone) { + if (initExpr->type && isDependent(ctx.unit, initExpr->type)) return; + ctx.error( initExpr->firstSourceLocation(), std::format("invalid initialization of reference of type '{}' from " diff --git a/tests/unit_tests/ast/template_lambda_01.cc b/tests/unit_tests/ast/template_lambda_01.cc index 7c9edfb1..6b166210 100644 --- a/tests/unit_tests/ast/template_lambda_01.cc +++ b/tests/unit_tests/ast/template_lambda_01.cc @@ -84,9 +84,9 @@ struct S { // CHECK-NEXT: return-statement // CHECK-NEXT: expression: binary-expression [prvalue type-param<0, 0>] // CHECK-NEXT: op: + -// CHECK-NEXT: left-expression: id-expression [lvalue T1] +// CHECK-NEXT: left-expression: id-expression [lvalue type-param<0, 1>] // CHECK-NEXT: unqualified-id: name-id // CHECK-NEXT: identifier: a -// CHECK-NEXT: right-expression: id-expression [lvalue T2] +// CHECK-NEXT: right-expression: id-expression [lvalue type-param<1, 1>] // CHECK-NEXT: unqualified-id: name-id // CHECK-NEXT: identifier: b diff --git a/tests/unit_tests/ast/template_member_expression_01.cc b/tests/unit_tests/ast/template_member_expression_01.cc index 01c9fb97..94bd4785 100644 --- a/tests/unit_tests/ast/template_member_expression_01.cc +++ b/tests/unit_tests/ast/template_member_expression_01.cc @@ -2,12 +2,12 @@ struct Allocator { template - auto Allocate(int) -> T * { + auto Allocate(int) -> T* { return nullptr; } }; -auto copy(Allocator &A) -> void * { return A.template Allocate(128); } +auto copy(Allocator& A) -> void* { return A.template Allocate(128); } // clang-format off // CHECK:translation-unit @@ -91,8 +91,8 @@ auto copy(Allocator &A) -> void * { return A.template Allocate(128); } // CHECK-NEXT: statement: compound-statement // CHECK-NEXT: statement-list // CHECK-NEXT: return-statement -// CHECK-NEXT: expression: call-expression -// CHECK-NEXT: base-expression: member-expression +// CHECK-NEXT: expression: call-expression [prvalue type-param<0, 0>*] +// CHECK-NEXT: base-expression: member-expression [lvalue type-param<0, 0>* (int)] // CHECK-NEXT: access-op: . // CHECK-NEXT: is-template-introduced: true // CHECK-NEXT: base-expression: id-expression [lvalue ::Allocator] diff --git a/tests/unit_tests/sema/conversion_op_template_01.cc b/tests/unit_tests/sema/conversion_op_template_01.cc new file mode 100644 index 00000000..b63d3480 --- /dev/null +++ b/tests/unit_tests/sema/conversion_op_template_01.cc @@ -0,0 +1,16 @@ +// RUN: %cxx -verify -fcheck %s +// expected-no-diagnostics + +struct Any { + template + operator T() const; +}; + +template +Any::operator T() const { return T(); } + +void test() { + Any a; + int i = a; + double d = a; +} diff --git a/tests/unit_tests/sema/dependent_ref_init_01.cc b/tests/unit_tests/sema/dependent_ref_init_01.cc new file mode 100644 index 00000000..fa344588 --- /dev/null +++ b/tests/unit_tests/sema/dependent_ref_init_01.cc @@ -0,0 +1,20 @@ +// RUN: %cxx -verify -fcheck %s +// expected-no-diagnostics + +template +void fwd_bind(T&& x) { + T& ref = x; + (void)ref; +} + +template +void lref_bind(T& x) { + T& ref = x; + (void)ref; +} + +void test() { + int i = 0; + fwd_bind(i); + lref_bind(i); +} diff --git a/tests/unit_tests/sema/implicit_member_call_01.cc b/tests/unit_tests/sema/implicit_member_call_01.cc new file mode 100644 index 00000000..20d8fc05 --- /dev/null +++ b/tests/unit_tests/sema/implicit_member_call_01.cc @@ -0,0 +1,30 @@ +// RUN: %cxx -verify -fcheck %s +// expected-no-diagnostics + +struct Counter { + int n = 0; + + void increment() { n++; } + void inc_twice() { + increment(); + increment(); + } + + void get() const { (void)n; } + void check() const { get(); } +}; + +struct Chain { + void a() {} + void b() { a(); } + void c() { b(); } +}; + +void test() { + Counter cnt; + cnt.inc_twice(); + cnt.check(); + + Chain ch; + ch.c(); +} diff --git a/tests/unit_tests/sema/member_access_on_non_class_in_template.cc b/tests/unit_tests/sema/member_access_on_non_class_in_template.cc new file mode 100644 index 00000000..04563b4d --- /dev/null +++ b/tests/unit_tests/sema/member_access_on_non_class_in_template.cc @@ -0,0 +1,20 @@ +// clang-format off +// RUN: %cxx -verify -fcheck %s + +template +void g(T t) { + // expected-error@1 {{invalid member access into expression of type 'int'}} + t.zero(); +} + +template +void f(T t) { + // expected-note@1 {{in instantiation of function template specialization 'g ' requested here}} + g(t); +} + +int main() { + // expected-note@1 {{in instantiation of function template specialization 'f ' requested here}} + f(0); + return 0; +} diff --git a/tests/unit_tests/sema/overload_qual_rank_01.cc b/tests/unit_tests/sema/overload_qual_rank_01.cc new file mode 100644 index 00000000..aee057a9 --- /dev/null +++ b/tests/unit_tests/sema/overload_qual_rank_01.cc @@ -0,0 +1,25 @@ +// RUN: %cxx -verify -fcheck %s +// expected-no-diagnostics + +namespace prefer_exact_over_qual { + +void f(int*); +void f(const int*); + +void test() { + int x = 0; + f(&x); +} + +} // namespace prefer_exact_over_qual + +namespace qual_conv_only_candidate { + +void g(const int*); + +void test() { + int x = 0; + g(&x); +} + +} // namespace qual_conv_only_candidate diff --git a/tests/unit_tests/sema/template_deduction_const_rref_01.cc b/tests/unit_tests/sema/template_deduction_const_rref_01.cc new file mode 100644 index 00000000..a22c4913 --- /dev/null +++ b/tests/unit_tests/sema/template_deduction_const_rref_01.cc @@ -0,0 +1,17 @@ +// RUN: %cxx -verify -fcheck %s +// expected-no-diagnostics + +template +void fwd(T&&) {} + +template +void crref(const T&&) {} + +void test() { + int i = 0; + const int ci = 0; + fwd(i); + fwd(ci); + fwd(0); + crref(0); +} diff --git a/tests/unit_tests/sema/template_deduction_const_rref_02.cc b/tests/unit_tests/sema/template_deduction_const_rref_02.cc new file mode 100644 index 00000000..9340f057 --- /dev/null +++ b/tests/unit_tests/sema/template_deduction_const_rref_02.cc @@ -0,0 +1,9 @@ +// RUN: not %cxx -fcheck %s + +template +void crref(const T&&) {} + +void test() { + int i = 0; + crref(i); +} diff --git a/tests/unit_tests/sema/template_deduction_ref_cv_01.cc b/tests/unit_tests/sema/template_deduction_ref_cv_01.cc new file mode 100644 index 00000000..0c65a18b --- /dev/null +++ b/tests/unit_tests/sema/template_deduction_ref_cv_01.cc @@ -0,0 +1,13 @@ +// RUN: %cxx -verify -fcheck %s +// expected-no-diagnostics + +template +T& ref_identity(T& t) { return t; } + +void test() { + int i = 0; + const int ci = 0; + + int& r1 = ref_identity(i); + const int& r2 = ref_identity(ci); +} diff --git a/tests/unit_tests/sema/template_fwd_default_args_01.cc b/tests/unit_tests/sema/template_fwd_default_args_01.cc new file mode 100644 index 00000000..cccdf5a4 --- /dev/null +++ b/tests/unit_tests/sema/template_fwd_default_args_01.cc @@ -0,0 +1,38 @@ +// RUN: %cxx -verify -fcheck %s + +// Default template arguments from forward declarations should be +// carried over to the definition when not repeated. + +template +struct S; + +template +struct S { + T* ptr = nullptr; +}; + +S<> a; // expected-type-is: S +S b; // expected-type-is: S + +// Non-type template parameter default from forward declaration. +template +struct V; + +template +struct V { + int value = N; +}; + +V<> c; +V<0> d; + +// Multiple parameters, some with defaults. +template +struct M; + +template +struct M {}; + +M e; +M f; +M g; diff --git a/tests/unit_tests/sema/template_fwd_default_args_02.cc b/tests/unit_tests/sema/template_fwd_default_args_02.cc new file mode 100644 index 00000000..863402d7 --- /dev/null +++ b/tests/unit_tests/sema/template_fwd_default_args_02.cc @@ -0,0 +1,25 @@ +// RUN: %cxx -verify -fcheck %s +// expected-no-diagnostics + +template +void func(); + +template +void func() {} + +template +struct S; + +template +struct S {}; + +template