https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/102857
>From 1119f0a8d180e482bff45c999d488827ac5ae49e Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Mon, 12 Aug 2024 23:32:34 +0800 Subject: [PATCH 01/13] [Clang][NFCI] Slightly refactor getTemplateInstantiationArgs() --- clang/include/clang/Sema/Sema.h | 12 +++++++++--- clang/lib/Sema/SemaConcept.cpp | 2 +- clang/lib/Sema/SemaTemplate.cpp | 2 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 15 +++++++-------- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2ec6367eccea01..352b26b0739ffb 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13033,11 +13033,14 @@ class Sema final : public SemaBase { /// instantiation arguments. /// /// \param DC In the event we don't HAVE a declaration yet, we instead provide - /// the decl context where it will be created. In this case, the `Innermost` - /// should likely be provided. If ND is non-null, this is ignored. + /// the decl context where it will be created. In this case, the \p + /// Innermost should likely be provided. If \p ND is non-null and \p + /// Innermost is NULL, this is ignored. /// /// \param Innermost if non-NULL, specifies a template argument list for the - /// template declaration passed as ND. + /// template declaration passed as \p ND. The next declaration context would + /// be switched to \p DC if present; otherwise, it would be the semantic + /// declaration context of \p ND. /// /// \param RelativeToPrimary true if we should get the template /// arguments relative to the primary template, even when we're @@ -13053,6 +13056,9 @@ class Sema final : public SemaBase { /// ForConstraintInstantiation indicates we should continue looking when /// encountering a lambda generic call operator, and continue looking for /// arguments on an enclosing class template. + /// + /// \param SkipForSpecialization when specified, any template specializations + /// in a traversal would be ignored. MultiLevelTemplateArgumentList getTemplateInstantiationArgs( const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false, std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt, diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index d4c9d044985e34..929555e94dc35d 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1482,7 +1482,7 @@ substituteParameterMappings(Sema &S, NormalizedConstraint &N, static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, const ConceptSpecializationExpr *CSE) { MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(), + CSE->getNamedConcept(), CSE->getNamedConcept()->getDeclContext(), /*Final=*/true, CSE->getTemplateArguments(), /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 1346a4a3f0012a..e6191c8c1397bd 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5582,7 +5582,7 @@ bool Sema::CheckTemplateArgumentList( CXXThisScopeRAII(*this, RD, ThisQuals, RD != nullptr); MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( - Template, NewContext, /*Final=*/true, SugaredConverted, + Template, Template->getDeclContext(), /*Final=*/true, SugaredConverted, /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConceptInstantiation=*/true); diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index de470739ab78e7..6cc9fb0ef04b65 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -491,7 +491,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( // has a depth of 0. if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl)) HandleDefaultTempArgIntoTempTempParam(TTP, Result); - CurDecl = Response::UseNextDecl(CurDecl).NextDecl; + CurDecl = DC ? Decl::castFromDeclContext(DC) + : Response::UseNextDecl(CurDecl).NextDecl; } while (!CurDecl->isFileContextDecl()) { @@ -3242,15 +3243,13 @@ bool Sema::SubstDefaultArgument( /*ForDefinition*/ false); if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs)) return true; - const FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate(); - if (PrimaryTemplate && PrimaryTemplate->isOutOfLine()) { - TemplateArgumentList *CurrentTemplateArgumentList = - TemplateArgumentList::CreateCopy(getASTContext(), - TemplateArgs.getInnermost()); + // FIXME: Investigate if we shall validate every FunctionTemplateDecl + // along the getInstantiatedFromMemberTemplate() chain. + if (auto *PrimaryTemplate = FD->getPrimaryTemplate(); + PrimaryTemplate && PrimaryTemplate->isOutOfLine()) NewTemplateArgs = getTemplateInstantiationArgs( FD, FD->getDeclContext(), /*Final=*/false, - CurrentTemplateArgumentList->asArray(), /*RelativeToPrimary=*/true); - } + TemplateArgs.getInnermost(), /*RelativeToPrimary=*/true); } runWithSufficientStackSpace(Loc, [&] { >From 2dd772d47a7330476173e0a34fb3176bd5c8d384 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Tue, 13 Aug 2024 09:41:13 +0800 Subject: [PATCH 02/13] Resolve a merge conflict --- clang/lib/Sema/SemaTemplate.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index ff29cf05dccff4..d2b1bc0e463ade 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5582,11 +5582,12 @@ bool Sema::CheckTemplateArgumentList( ContextRAII Context(*this, NewContext); CXXThisScopeRAII(*this, RD, ThisQuals, RD != nullptr); - MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( - Template, Template->getDeclContext(), /*Final=*/false, SugaredConverted, - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConceptInstantiation=*/true); + MultiLevelTemplateArgumentList MLTAL = + getTemplateInstantiationArgs(Template, Template->getDeclContext(), + /*Final=*/false, CanonicalConverted, + /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConceptInstantiation=*/true); if (EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { >From 96bf64c230b3a854ff09b2f7f32340bf17679746 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sun, 11 Aug 2024 16:51:34 +0800 Subject: [PATCH 03/13] CWG 2369 --- clang/include/clang/Sema/Sema.h | 3 +- clang/lib/Sema/SemaConcept.cpp | 2 +- clang/lib/Sema/SemaTemplateDeduction.cpp | 94 ++++++++++++++++--- clang/lib/Sema/SemaTemplateInstantiate.cpp | 5 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 48 +++++++--- 5 files changed, 122 insertions(+), 30 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c1912343cf43b8..48901ded906ee8 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13064,7 +13064,8 @@ class Sema final : public SemaBase { std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt, bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, bool ForConstraintInstantiation = false, - bool SkipForSpecialization = false); + bool SkipForSpecialization = false, + MultiLevelTemplateArgumentList *Merged = nullptr); /// RAII object to handle the state changes required to synthesize /// a function body. diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index f4dd0648781b61..79057e0e3e8138 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -847,7 +847,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, bool ForOverloadResolution) { // Don't check constraints if the function is dependent. Also don't check if // this is a function template specialization, as the call to - // CheckinstantiatedFunctionTemplateConstraints after this will check it + // CheckInstantiatedFunctionTemplateConstraints after this will check it // better. if (FD->isDependentContext() || FD->getTemplatedKind() == diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index ec951d5ac06dbc..9bb215fe66c5a6 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3834,18 +3834,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( Result != TemplateDeductionResult::Success) return Result; - // C++ [temp.deduct.call]p10: [DR1391] - // If deduction succeeds for all parameters that contain - // template-parameters that participate in template argument deduction, - // and all template arguments are explicitly specified, deduced, or - // obtained from default template arguments, remaining parameters are then - // compared with the corresponding arguments. For each remaining parameter - // P with a type that was non-dependent before substitution of any - // explicitly-specified template arguments, if the corresponding argument - // A cannot be implicitly converted to P, deduction fails. - if (CheckNonDependent()) - return TemplateDeductionResult::NonDependentConversionFailure; - // Form the template argument list from the deduced template arguments. TemplateArgumentList *SugaredDeducedArgumentList = TemplateArgumentList::CreateCopy(Context, SugaredBuilder); @@ -3875,6 +3863,76 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( FD = const_cast<FunctionDecl *>(FDFriend); Owner = FD->getLexicalDeclContext(); } +#if 1 + // FIXME: We have to partially instantiate lambda's captures for constraint + // evaluation. + if (!isLambdaCallOperator(FD) && !isLambdaConversionOperator(FD) && + (!PartialOverloading || + (CanonicalBuilder.size() == + FunctionTemplate->getTemplateParameters()->size()))) { + FunctionTemplateDecl *Template = FunctionTemplate->getCanonicalDecl(); + FunctionDecl *FD = Template->getTemplatedDecl(); + SmallVector<const Expr *, 3> TemplateAC; + Template->getAssociatedConstraints(TemplateAC); + if (!TemplateAC.empty()) { + + // Enter the scope of this instantiation. We don't use + // PushDeclContext because we don't have a scope. + LocalInstantiationScope Scope(*this); + + // Collect the list of template arguments relative to the 'primary' + // template. We need the entire list, since the constraint is completely + // uninstantiated at this point. + + MultiLevelTemplateArgumentList MLTAL(FD, SugaredBuilder, /*Final=*/false); + getTemplateInstantiationArgs(nullptr, FD->getLexicalDeclContext(), + /*Final=*/false, + /*Innermost=*/std::nullopt, + /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConstraintInstantiation=*/true, + /*SkipForSpecialization=*/false, + /*Merged=*/&MLTAL); + + // if (SetupConstraintScope(FD, SugaredBuilder, MLTAL, Scope)) + // return TemplateDeductionResult::MiscellaneousDeductionFailure; + + MultiLevelTemplateArgumentList JustTemplArgs( + Template, CanonicalDeducedArgumentList->asArray(), + /*Final=*/false); + if (addInstantiatedParametersToScope(nullptr, FD, Scope, JustTemplArgs)) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + + if (FunctionTemplateDecl *FromMemTempl = + Template->getInstantiatedFromMemberTemplate()) { + while (FromMemTempl->getInstantiatedFromMemberTemplate()) + FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate(); + if (addInstantiatedParametersToScope( + nullptr, FromMemTempl->getTemplatedDecl(), Scope, MLTAL)) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + } + + Qualifiers ThisQuals; + CXXRecordDecl *Record = nullptr; + if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) { + ThisQuals = Method->getMethodQualifiers(); + Record = Method->getParent(); + } + CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); + llvm::SmallVector<Expr *, 1> Converted; + if (CheckConstraintSatisfaction(Template, TemplateAC, MLTAL, + Template->getSourceRange(), + Info.AssociatedConstraintsSatisfaction)) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { + Info.reset(TemplateArgumentList::CreateCopy(Context, SugaredBuilder), + Info.takeCanonical()); + return TemplateDeductionResult::ConstraintsNotSatisfied; + } + } + } +#endif + MultiLevelTemplateArgumentList SubstArgs( FunctionTemplate, CanonicalDeducedArgumentList->asArray(), /*Final=*/false); @@ -3924,6 +3982,18 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( } } + // C++ [temp.deduct.call]p10: [DR1391] + // If deduction succeeds for all parameters that contain + // template-parameters that participate in template argument deduction, + // and all template arguments are explicitly specified, deduced, or + // obtained from default template arguments, remaining parameters are then + // compared with the corresponding arguments. For each remaining parameter + // P with a type that was non-dependent before substitution of any + // explicitly-specified template arguments, if the corresponding argument + // A cannot be implicitly converted to P, deduction fails. + if (CheckNonDependent()) + return TemplateDeductionResult::NonDependentConversionFailure; + // We skipped the instantiation of the explicit-specifier during the // substitution of `FD` before. So, we try to instantiate it back if // `Specialization` is either a constructor or a conversion function. diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index efae547b21c715..6a6ea8849badc8 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -468,10 +468,11 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( const NamedDecl *ND, const DeclContext *DC, bool Final, std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary, const FunctionDecl *Pattern, bool ForConstraintInstantiation, - bool SkipForSpecialization) { + bool SkipForSpecialization, MultiLevelTemplateArgumentList *Merged) { assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); // Accumulate the set of template argument lists in this structure. - MultiLevelTemplateArgumentList Result; + MultiLevelTemplateArgumentList Ret; + MultiLevelTemplateArgumentList &Result = Merged ? *Merged : Ret; using namespace TemplateInstArgsHelpers; const Decl *CurDecl = ND; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index f93cd113988ae4..5a7cb294a1c86d 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4577,17 +4577,17 @@ void Sema::addInstantiatedLocalVarsToScope(FunctionDecl *Function, } } -bool Sema::addInstantiatedParametersToScope( - FunctionDecl *Function, const FunctionDecl *PatternDecl, - LocalInstantiationScope &Scope, +static bool addInstantiatedParametersToScope( + Sema &SemaRef, MutableArrayRef<ParmVarDecl *> InstantiatedParamDecls, + const FunctionDecl *PatternDecl, LocalInstantiationScope &Scope, const MultiLevelTemplateArgumentList &TemplateArgs) { unsigned FParamIdx = 0; for (unsigned I = 0, N = PatternDecl->getNumParams(); I != N; ++I) { const ParmVarDecl *PatternParam = PatternDecl->getParamDecl(I); if (!PatternParam->isParameterPack()) { // Simple case: not a parameter pack. - assert(FParamIdx < Function->getNumParams()); - ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); + assert(FParamIdx < InstantiatedParamDecls.size()); + ParmVarDecl *FunctionParam = InstantiatedParamDecls[FParamIdx]; FunctionParam->setDeclName(PatternParam->getDeclName()); // If the parameter's type is not dependent, update it to match the type // in the pattern. They can differ in top-level cv-qualifiers, and we want @@ -4596,9 +4596,9 @@ bool Sema::addInstantiatedParametersToScope( // it's instantiation-dependent. // FIXME: Updating the type to work around this is at best fragile. if (!PatternDecl->getType()->isDependentType()) { - QualType T = SubstType(PatternParam->getType(), TemplateArgs, - FunctionParam->getLocation(), - FunctionParam->getDeclName()); + QualType T = SemaRef.SubstType(PatternParam->getType(), TemplateArgs, + FunctionParam->getLocation(), + FunctionParam->getDeclName()); if (T.isNull()) return true; FunctionParam->setType(T); @@ -4612,18 +4612,19 @@ bool Sema::addInstantiatedParametersToScope( // Expand the parameter pack. Scope.MakeInstantiatedLocalArgPack(PatternParam); std::optional<unsigned> NumArgumentsInExpansion = - getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs); + SemaRef.getNumArgumentsInExpansion(PatternParam->getType(), + TemplateArgs); if (NumArgumentsInExpansion) { QualType PatternType = PatternParam->getType()->castAs<PackExpansionType>()->getPattern(); for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) { - ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); + ParmVarDecl *FunctionParam = InstantiatedParamDecls[FParamIdx]; FunctionParam->setDeclName(PatternParam->getDeclName()); if (!PatternDecl->getType()->isDependentType()) { - Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(*this, Arg); - QualType T = - SubstType(PatternType, TemplateArgs, FunctionParam->getLocation(), - FunctionParam->getDeclName()); + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, Arg); + QualType T = SemaRef.SubstType(PatternType, TemplateArgs, + FunctionParam->getLocation(), + FunctionParam->getDeclName()); if (T.isNull()) return true; FunctionParam->setType(T); @@ -4638,6 +4639,25 @@ bool Sema::addInstantiatedParametersToScope( return false; } +bool Sema::addInstantiatedParametersToScope( + FunctionDecl *Function, const FunctionDecl *PatternDecl, + LocalInstantiationScope &Scope, + const MultiLevelTemplateArgumentList &TemplateArgs) { + if (Function) + return ::addInstantiatedParametersToScope(*this, Function->parameters(), + PatternDecl, Scope, TemplateArgs); + FunctionTypeLoc TypeLoc = PatternDecl->getFunctionTypeLoc(); + assert(!TypeLoc.isNull() && "Invalid function TypeLoc?"); + SmallVector<QualType> ParamTypes; + SmallVector<ParmVarDecl *> OutParams; + Sema::ExtParameterInfoBuilder ExtParamInfos; + if (SubstParmTypes(PatternDecl->getLocation(), TypeLoc.getParams(), nullptr, + TemplateArgs, ParamTypes, &OutParams, ExtParamInfos)) + return true; + return ::addInstantiatedParametersToScope(*this, OutParams, PatternDecl, + Scope, TemplateArgs); +} + bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD, ParmVarDecl *Param) { assert(Param->hasUninstantiatedDefaultArg()); >From 6b7072d713c163e0459fdd5493c25009f990ec09 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Mon, 12 Aug 2024 15:22:42 +0800 Subject: [PATCH 04/13] Rectify diagnostics --- clang/lib/Sema/SemaTemplateDeduction.cpp | 32 ++++++++----------- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 8 +++-- clang/test/CXX/drs/cwg23xx.cpp | 32 +++++++++++++++++++ clang/test/CXX/drs/cwg26xx.cpp | 2 +- .../expr.prim.req/nested-requirement.cpp | 2 +- .../constrant-satisfaction-conversions.cpp | 6 ++-- .../SemaCXX/concept-crash-on-diagnostic.cpp | 2 +- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 2 +- clang/test/SemaCXX/cxx23-assume.cpp | 6 ++-- clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 2 +- clang/test/SemaCXX/lambda-unevaluated.cpp | 4 +-- .../SemaTemplate/concepts-recursive-inst.cpp | 4 +-- .../SemaTemplate/cxx2a-constraint-exprs.cpp | 2 +- clang/test/SemaTemplate/deduction-guide.cpp | 5 --- .../nested-implicit-deduction-guides.cpp | 8 +++-- 15 files changed, 72 insertions(+), 45 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 9bb215fe66c5a6..1d475084afe846 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3863,7 +3863,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( FD = const_cast<FunctionDecl *>(FDFriend); Owner = FD->getLexicalDeclContext(); } -#if 1 + // [DR2369] // FIXME: We have to partially instantiate lambda's captures for constraint // evaluation. if (!isLambdaCallOperator(FD) && !isLambdaConversionOperator(FD) && @@ -3894,9 +3894,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( /*SkipForSpecialization=*/false, /*Merged=*/&MLTAL); - // if (SetupConstraintScope(FD, SugaredBuilder, MLTAL, Scope)) - // return TemplateDeductionResult::MiscellaneousDeductionFailure; - MultiLevelTemplateArgumentList JustTemplArgs( Template, CanonicalDeducedArgumentList->asArray(), /*Final=*/false); @@ -3921,7 +3918,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); llvm::SmallVector<Expr *, 1> Converted; if (CheckConstraintSatisfaction(Template, TemplateAC, MLTAL, - Template->getSourceRange(), + Info.getLocation(), Info.AssociatedConstraintsSatisfaction)) return TemplateDeductionResult::MiscellaneousDeductionFailure; if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { @@ -3931,7 +3928,18 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( } } } -#endif + + // C++ [temp.deduct.call]p10: [DR1391] + // If deduction succeeds for all parameters that contain + // template-parameters that participate in template argument deduction, + // and all template arguments are explicitly specified, deduced, or + // obtained from default template arguments, remaining parameters are then + // compared with the corresponding arguments. For each remaining parameter + // P with a type that was non-dependent before substitution of any + // explicitly-specified template arguments, if the corresponding argument + // A cannot be implicitly converted to P, deduction fails. + if (CheckNonDependent()) + return TemplateDeductionResult::NonDependentConversionFailure; MultiLevelTemplateArgumentList SubstArgs( FunctionTemplate, CanonicalDeducedArgumentList->asArray(), @@ -3982,18 +3990,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( } } - // C++ [temp.deduct.call]p10: [DR1391] - // If deduction succeeds for all parameters that contain - // template-parameters that participate in template argument deduction, - // and all template arguments are explicitly specified, deduced, or - // obtained from default template arguments, remaining parameters are then - // compared with the corresponding arguments. For each remaining parameter - // P with a type that was non-dependent before substitution of any - // explicitly-specified template arguments, if the corresponding argument - // A cannot be implicitly converted to P, deduction fails. - if (CheckNonDependent()) - return TemplateDeductionResult::NonDependentConversionFailure; - // We skipped the instantiation of the explicit-specifier during the // substitution of `FD` before. So, we try to instantiate it back if // `Specialization` is either a constructor or a conversion function. diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index 545da21183c3c4..405a733550fd54 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -904,10 +904,12 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef, Context.getTrivialTypeSourceInfo( Context.getDeducedTemplateSpecializationType( TemplateName(AliasTemplate), /*DeducedType=*/QualType(), - /*IsDependent=*/true)), // template specialization type whose - // arguments will be deduced. + /*IsDependent=*/true), + AliasTemplate->getLocation()), // template specialization type whose + // arguments will be deduced. Context.getTrivialTypeSourceInfo( - ReturnType), // type from which template arguments are deduced. + ReturnType, AliasTemplate->getLocation()), // type from which template + // arguments are deduced. }; return TypeTraitExpr::Create( Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(), diff --git a/clang/test/CXX/drs/cwg23xx.cpp b/clang/test/CXX/drs/cwg23xx.cpp index 77fd6a337436e3..4685f2fec2c749 100644 --- a/clang/test/CXX/drs/cwg23xx.cpp +++ b/clang/test/CXX/drs/cwg23xx.cpp @@ -491,3 +491,35 @@ namespace cwg2397 { // cwg2397: 17 } // namespace cwg2397 #endif + +#if __cplusplus >= 202002L + +namespace cwg2369 { // cwg2369: 20 + +template <class T> struct Z { + typedef typename T::x xx; +}; + +template <class T> +concept C = requires { typename T::A; }; +template <C T> typename Z<T>::xx f(void *, T); // #1 +template <class T> void f(int, T); // #2 + +struct A { +} a; + +struct ZZ { + template <class T, class = typename Z<T>::xx> operator T *(); + operator int(); +}; + +void foo() { + ZZ zz; + f(1, a); // OK, deduction fails for #1 because there is no conversion from int + // to void* + f(zz, 42); // OK, deduction fails for #1 because C<int> is not satisfied +} + +} // namespace cwg2369 + +#endif diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp index 63a954c803b77a..35814ca9a26a39 100644 --- a/clang/test/CXX/drs/cwg26xx.cpp +++ b/clang/test/CXX/drs/cwg26xx.cpp @@ -319,7 +319,7 @@ void f(T) requires requires { []() { T::invalid; } (); }; // since-cxx20-note@-3 {{in instantiation of requirement here}} // since-cxx20-note@-4 {{while substituting template arguments into constraint expression here}} // since-cxx20-note@#cwg2672-f-0 {{while checking constraint satisfaction for template 'f<int>' required here}} -// since-cxx20-note@#cwg2672-f-0 {{in instantiation of function template specialization 'cwg2672::f<int>' requested here}} +// since-cxx20-note@#cwg2672-f-0 {{while substituting deduced template arguments into function template 'f' [with T = int]}} void f(...); template <class T> diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp index 763d983d20f615..a23f7dc595171e 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp @@ -154,7 +154,7 @@ void func() { bar<int>(); // expected-note@-1 {{while checking constraint satisfaction for template 'bar<int>' required here}} \ - // expected-note@-1 {{in instantiation of function template specialization}} + // expected-note@-1 {{while substituting deduced template arguments into function template 'bar' [with T = int]}} // expected-note@#bar {{in instantiation of static data member}} // expected-note@#bar {{in instantiation of requirement here}} // expected-note@#bar {{while checking the satisfaction of nested requirement requested here}} diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp index ba8e2dc372e984..c41de77986bcae 100644 --- a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp @@ -11,7 +11,7 @@ template<typename T> struct S { // expected-error@+3{{atomic constraint must be of type 'bool' (found 'S<int>')}} // expected-note@#FINST{{while checking constraint satisfaction}} -// expected-note@#FINST{{in instantiation of function template specialization}} +// expected-note@#FINST{{while substituting deduced template arguments into function template 'f' [with T = int]}} template<typename T> requires (S<T>{}) void f(T); void f(int); @@ -19,7 +19,7 @@ void f(int); // Ensure this applies to operator && as well. // expected-error@+3{{atomic constraint must be of type 'bool' (found 'S<int>')}} // expected-note@#F2INST{{while checking constraint satisfaction}} -// expected-note@#F2INST{{in instantiation of function template specialization}} +// expected-note@#F2INST{{while substituting deduced template arguments into function template 'f2' [with T = int]}} template<typename T> requires (S<T>{} && true) void f2(T); void f2(int); @@ -32,7 +32,7 @@ template<typename T> requires requires { // expected-note@-4{{while checking the satisfaction}} // expected-note@-6{{while substituting template arguments}} // expected-note@#F3INST{{while checking constraint satisfaction}} - // expected-note@#F3INST{{in instantiation of function template specialization}} + // expected-note@#F3INST{{while substituting deduced template arguments into function template 'f3' [with T = int]}} // } void f3(T); diff --git a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp index 71e55c8290ee4a..ccc109cbca0f19 100644 --- a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp +++ b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp @@ -31,7 +31,7 @@ void function() { // expected-note@#3 {{checking the satisfaction of concept 'convertible_to<bool, bool>'}} // expected-note@#2 {{substituting template arguments into constraint expression here}} // expected-note@#5 {{checking constraint satisfaction for template 'compare<Object *, Object *>'}} -// expected-note@#5 {{in instantiation of function template specialization 'compare<Object *, Object *>' requested here}} +// expected-note@#5 {{while substituting deduced template arguments into function template 'compare' [with IteratorL = Object *, IteratorR = Object *]}} // expected-note@#4 {{candidate template ignored: constraints not satisfied [with IteratorL = Object *, IteratorR = Object *]}} // We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted. diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 5392573fcdb9d5..dff7f611635582 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -196,7 +196,7 @@ struct Foo { template <int K> using Bar = Foo<double, K>; // expected-note {{constraints not satisfied for class template 'Foo'}} -// expected-note@-1 {{candidate template ignored: could not match}} +// expected-note@-1 {{candidate template ignored: could not match}} expected-note@-1 {{candidate template ignored: constraints not satisfied}} // expected-note@-2 {{implicit deduction guide declared as 'template <int K> requires __is_deducible(test14::Bar, Foo<double, K>) Bar(Foo<double, K>) -> Foo<double, K>'}} // expected-note@-3 {{implicit deduction guide declared as 'template <int K> requires __is_deducible(test14::Bar, Foo<double, K>) Bar(const double (&)[K]) -> Foo<double, K>'}} double abc[3]; diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp index 9138501d726dd6..35971a58331ff3 100644 --- a/clang/test/SemaCXX/cxx23-assume.cpp +++ b/clang/test/SemaCXX/cxx23-assume.cpp @@ -127,12 +127,12 @@ constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while che static_assert(f5<int>() == 1); static_assert(f5<D>() == 1); // expected-note 3 {{while checking constraint satisfaction}} - // expected-note@-1 3 {{in instantiation of}} + // expected-note@-1 3 {{while substituting deduced template arguments}} // expected-error@-2 {{no matching function for call}} static_assert(f5<double>() == 2); -static_assert(f5<E>() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}} -static_assert(f5<F>() == 2); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}} +static_assert(f5<E>() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{while substituting deduced template arguments}} +static_assert(f5<F>() == 2); // expected-note {{while checking constraint satisfaction}} expected-note {{while substituting deduced template arguments}} // Do not validate assumptions whose evaluation would have side-effects. constexpr int foo() { diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp index 0674135aac483f..70644e3aef8a71 100644 --- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp +++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp @@ -233,7 +233,7 @@ void g() { A<Thingy, Thingy> *ap; f(ap, ap); // expected-error{{no matching function for call to 'f'}} \ // expected-note {{while checking constraint satisfaction}} \ - // expected-note {{in instantiation of function template specialization}} + // expected-note {{while substituting deduced template arguments}} } } diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp b/clang/test/SemaCXX/lambda-unevaluated.cpp index 39ee89bc797f84..242d1fc0c1219d 100644 --- a/clang/test/SemaCXX/lambda-unevaluated.cpp +++ b/clang/test/SemaCXX/lambda-unevaluated.cpp @@ -172,7 +172,7 @@ int* func(T) requires requires { []() { T::foo(); }; }; // expected-error{{type double* func(...); static_assert(__is_same(decltype(func(0)), double*)); // expected-note {{while checking constraint satisfaction for template 'func<int>' required here}} - // expected-note@-1 {{in instantiation of function template specialization 'lambda_in_constraints::func<int>'}} + // expected-note@-1 {{while substituting deduced template arguments into function template 'func' [with T = int]}} static_assert(__is_same(decltype(func(WithFoo())), int*)); template <class T> @@ -250,7 +250,7 @@ S s("a"); // #use // expected-note@#S-requires {{substituting template arguments into constraint expression here}} // expected-note@#S-requires {{in instantiation of requirement here}} // expected-note@#use {{checking constraint satisfaction for template 'S<const char *>' required here}} -// expected-note@#use {{requested here}} +// expected-note@#use {{while substituting deduced template arguments into function template 'S' [with value:auto = const char *]}} // expected-note-re@#S 2{{candidate constructor {{.*}} not viable}} // expected-note@#S-ctor {{constraints not satisfied}} // expected-note-re@#S-requires {{because {{.*}} would be invalid}} diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp b/clang/test/SemaTemplate/concepts-recursive-inst.cpp index 9330df8cdd0398..30a410cef91ee9 100644 --- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp +++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp @@ -76,7 +76,7 @@ auto it = begin(rng); // #BEGIN_CALL // expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<DirectRecursiveCheck::my_range>' requested here}} // expected-note@#INF_BEGIN {{while substituting template arguments into constraint expression here}} // expected-note@#BEGIN_CALL {{while checking constraint satisfaction for template 'begin<DirectRecursiveCheck::my_range>' required here}} -// expected-note@#BEGIN_CALL {{in instantiation of function template specialization}} +// expected-note@#BEGIN_CALL {{while substituting deduced template arguments into function template}} // Fallout of the failure is failed lookup, which is necessary to stop odd // cascading errors. @@ -103,7 +103,7 @@ namespace GH50891 { // expected-note@#OP_TO {{while checking the satisfaction of concept 'Numeric<GH50891::Deferred>' requested here}} // expected-note@#OP_TO {{while substituting template arguments into constraint expression here}} // expected-note@#FOO_CALL {{while checking constraint satisfaction for template}} - // expected-note@#FOO_CALL {{in instantiation of function template specialization}} + // expected-note@#FOO_CALL {{while substituting deduced template arguments into function template}} // expected-note@#FOO_CALL {{in instantiation of requirement here}} // expected-note@#NUMERIC {{while substituting template arguments into constraint expression here}} diff --git a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp index f4403587a62594..5809ef684bbf3b 100644 --- a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp +++ b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp @@ -34,7 +34,7 @@ namespace constant_evaluated { expected-note@-1{{candidate template ignored}} int a = (foo<int>(), 0); // expected-note@-1 {{while checking}} expected-error@-1{{no matching function}} \ - expected-note@-1 {{in instantiation}} + expected-note@-1 {{while substituting}} template<typename T> void bar() requires requires { requires f<int[2]>; } { }; // expected-note@-1{{in instantiation}} \ expected-note@-1{{while substituting}} \ diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp index d03c783313dd71..67d00bb49f77d7 100644 --- a/clang/test/SemaTemplate/deduction-guide.cpp +++ b/clang/test/SemaTemplate/deduction-guide.cpp @@ -234,11 +234,6 @@ F s(0); // CHECK: | `-CXXBoolLiteralExpr {{.*}} 'bool' false // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 'auto (U) -> F<>' // CHECK: | `-ParmVarDecl {{.*}} 'U' -// CHECK: `-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 'auto (int) -> F<>' -// CHECK: |-TemplateArgument integral ''x'' -// CHECK: |-TemplateArgument type 'int' -// CHECK: | `-BuiltinType {{.*}} 'int' -// CHECK: `-ParmVarDecl {{.*}} 'int' // CHECK: FunctionProtoType {{.*}} 'auto (U) -> F<>' dependent trailing_return cdecl // CHECK: |-InjectedClassNameType {{.*}} 'F<>' dependent // CHECK: | `-CXXRecord {{.*}} 'F' diff --git a/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp index af3e3358f61382..5c7a90273d0e0f 100644 --- a/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp +++ b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp @@ -38,7 +38,7 @@ template<typename A, typename T> concept True = true; template<typename T> -concept False = false; +concept False = false; // #False template<class X> struct concepts { template<class Y> struct B { @@ -68,7 +68,7 @@ template<typename X> struct nested_init_list { Y y; }; - template<False F> + template<False F> // #INIT_LIST_INNER_INVALID_HEADER struct concept_fail { // #INIT_LIST_INNER_INVALID X x; F f; @@ -81,7 +81,9 @@ using NIL = nested_init_list<int>::B<int>; // expected-error@+1 {{no viable constructor or deduction guide for deduction of template arguments of 'nested_init_list<int>::concept_fail'}} nested_init_list<int>::concept_fail nil_invalid{1, ""}; -// expected-note@#INIT_LIST_INNER_INVALID {{candidate template ignored: substitution failure [with F = const char *]: constraints not satisfied for class template 'concept_fail' [with F = const char *]}} +// expected-note@#INIT_LIST_INNER_INVALID {{candidate template ignored: constraints not satisfied [with F = const char *]}} +// expected-note@#INIT_LIST_INNER_INVALID_HEADER {{because 'const char *' does not satisfy 'False'}} +// expected-note@#False {{because 'false' evaluated to false}} // expected-note@#INIT_LIST_INNER_INVALID {{implicit deduction guide declared as 'template <False F> concept_fail(int, F) -> concept_fail<F>'}} // expected-note@#INIT_LIST_INNER_INVALID {{candidate function template not viable: requires 1 argument, but 2 were provided}} // expected-note@#INIT_LIST_INNER_INVALID {{implicit deduction guide declared as 'template <False F> concept_fail(concept_fail<F>) -> concept_fail<F>'}} >From bb31d36a92fb821d551ea8465941b93f590af00f Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Mon, 12 Aug 2024 23:34:33 +0800 Subject: [PATCH 05/13] Instantiate function parameters as needed --- clang/include/clang/Sema/Sema.h | 5 + clang/lib/Sema/SemaConcept.cpp | 137 ++++++++++++++++++ clang/lib/Sema/SemaTemplateDeduction.cpp | 82 +++-------- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 53 +++---- clang/lib/Sema/TreeTransform.h | 2 +- libcxx/include/__chrono/leap_second.h | 2 +- 6 files changed, 179 insertions(+), 102 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 48901ded906ee8..33eff47a8707bf 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -14450,6 +14450,11 @@ class Sema final : public SemaBase { ArrayRef<TemplateArgument> TemplateArgs, ConstraintSatisfaction &Satisfaction); + bool CheckFunctionConstraintsWithoutInstantiation( + SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template, + ArrayRef<TemplateArgument> TemplateArgs, + ConstraintSatisfaction &Satisfaction); + /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. /// \param First whether this is the first time an unsatisfied constraint is diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 79057e0e3e8138..ad17d714037ed0 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -461,13 +461,17 @@ static ExprResult calculateConstraintSatisfaction( return ExprError(); llvm::FoldingSetNodeID ID; + // llvm::errs() << "Preparing for checking " << Template << "\n"; if (Template && DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) { + // Template->dump(); Satisfaction.IsSatisfied = false; Satisfaction.ContainsErrors = true; + // __builtin_trap(); return ExprEmpty(); } + // llvm::errs() << "Pushing " << Template << "\n"; SatisfactionStackRAII StackRAII(S, Template, ID); // We do not want error diagnostics escaping here. @@ -1122,6 +1126,139 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( PointOfInstantiation, Satisfaction); } +namespace { + +// We employ a TreeTransform because RAV couldn't recurse into a bunch of +// Exprs e.g. SizeOfPackExpr, CXXFoldExpr, etc. +// FIXME: Could we do the Decl instantiation as we substitute into +// the constraint expressions? +class InstantiateReferencedParameter + : public TreeTransform<InstantiateReferencedParameter> { + const MultiLevelTemplateArgumentList &TemplateArgs; + + DeclContext *FunctionDC; + + using inherited = TreeTransform<InstantiateReferencedParameter>; + + bool instantiateParameterToScope(ParmVarDecl *OldParm, + LocalInstantiationScope &Scope) { + // Current context might have been changed by lambda expressions. So resume + // it before we substitute parameters. + Sema::ContextRAII Context(SemaRef, FunctionDC); + std::optional<unsigned> NumExpansions; + ParmVarDecl *NewParm = nullptr; + unsigned IndexAdjustment = 0; + if (OldParm->isParameterPack()) { + SmallVector<UnexpandedParameterPack, 2> Unexpanded; + TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc(); + PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>(); + TypeLoc Pattern = ExpansionTL.getPatternLoc(); + SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); + + assert(!Unexpanded.empty() && + "A pack Decl doesn't contain anything unexpanded?"); + + bool ShouldExpand = false; + bool RetainExpansion = false; + std::optional<unsigned> OrigNumExpansions = + ExpansionTL.getTypePtr()->getNumExpansions(); + NumExpansions = OrigNumExpansions; + if (SemaRef.CheckParameterPacksForExpansion( + ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(), + Unexpanded, TemplateArgs, ShouldExpand, RetainExpansion, + NumExpansions)) + return true; + + assert(ShouldExpand && !RetainExpansion && + "Shouldn't retain an expansion here!"); + Scope.MakeInstantiatedLocalArgPack(OldParm); + + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I); + ParmVarDecl *NewParm = SemaRef.SubstParmVarDecl( + OldParm, TemplateArgs, /*indexAdjustment=*/IndexAdjustment++, + NumExpansions, /*ExpectParameterPack=*/false, + /*EvaluateConstraints=*/false); + if (!NewParm) + return true; + } + + return false; + } + NewParm = SemaRef.SubstParmVarDecl(OldParm, TemplateArgs, + /*indexAdjustment=*/IndexAdjustment, + std::nullopt, + /*ExpectParameterPack=*/false); + if (!NewParm) + return true; + Scope.InstantiatedLocal(OldParm, NewParm); + return false; + } + +public: + InstantiateReferencedParameter( + Sema &SemaRef, const MultiLevelTemplateArgumentList &TemplateArgs, + DeclContext *FunctionDC) + : inherited(SemaRef), TemplateArgs(TemplateArgs), FunctionDC(FunctionDC) { + } + + Decl *TransformDecl(SourceLocation Loc, Decl *D) { + if (auto *PVD = dyn_cast<ParmVarDecl>(D)) + instantiateParameterToScope(PVD, *SemaRef.CurrentInstantiationScope); + return D; + } + + void TraverseConstraintExprs(ArrayRef<const Expr *> Exprs) { + for (auto *E : Exprs) + TransformExpr(const_cast<Expr *>(E)); + } +}; + +} // namespace + +bool Sema::CheckFunctionConstraintsWithoutInstantiation( + SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template, + ArrayRef<TemplateArgument> TemplateArgs, + ConstraintSatisfaction &Satisfaction) { + FunctionDecl *FD = Template->getTemplatedDecl(); + SmallVector<const Expr *, 3> TemplateAC; + Template->getAssociatedConstraints(TemplateAC); + if (TemplateAC.empty()) { + Satisfaction.IsSatisfied = true; + return false; + } + + // Enter the scope of this instantiation. We don't use + // PushDeclContext because we don't have a scope. + LocalInstantiationScope Scope(*this); + + // Collect the list of template arguments relative to the 'primary' + // template. We need the entire list, since the constraint is completely + // uninstantiated at this point. + DeclContext *NextDC = FD->getFriendObjectKind() ? FD->getLexicalDeclContext() + : FD->getDeclContext(); + MultiLevelTemplateArgumentList MLTAL = + getTemplateInstantiationArgs(FD, NextDC, + /*Final=*/false, + /*Innermost=*/TemplateArgs, + /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConstraintInstantiation=*/true); + + InstantiateReferencedParameter(*this, MLTAL, FD) + .TraverseConstraintExprs(TemplateAC); + + Qualifiers ThisQuals; + CXXRecordDecl *Record = nullptr; + if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) { + ThisQuals = Method->getMethodQualifiers(); + Record = Method->getParent(); + } + CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); + return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL, + PointOfInstantiation, Satisfaction); +} + static void diagnoseUnsatisfiedRequirement(Sema &S, concepts::ExprRequirement *Req, bool First) { diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 1d475084afe846..f1883882edc804 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3863,72 +3863,28 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( FD = const_cast<FunctionDecl *>(FDFriend); Owner = FD->getLexicalDeclContext(); } + // [DR2369] // FIXME: We have to partially instantiate lambda's captures for constraint // evaluation. - if (!isLambdaCallOperator(FD) && !isLambdaConversionOperator(FD) && - (!PartialOverloading || - (CanonicalBuilder.size() == - FunctionTemplate->getTemplateParameters()->size()))) { - FunctionTemplateDecl *Template = FunctionTemplate->getCanonicalDecl(); - FunctionDecl *FD = Template->getTemplatedDecl(); - SmallVector<const Expr *, 3> TemplateAC; - Template->getAssociatedConstraints(TemplateAC); - if (!TemplateAC.empty()) { - - // Enter the scope of this instantiation. We don't use - // PushDeclContext because we don't have a scope. - LocalInstantiationScope Scope(*this); - - // Collect the list of template arguments relative to the 'primary' - // template. We need the entire list, since the constraint is completely - // uninstantiated at this point. - - MultiLevelTemplateArgumentList MLTAL(FD, SugaredBuilder, /*Final=*/false); - getTemplateInstantiationArgs(nullptr, FD->getLexicalDeclContext(), - /*Final=*/false, - /*Innermost=*/std::nullopt, - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true, - /*SkipForSpecialization=*/false, - /*Merged=*/&MLTAL); - - MultiLevelTemplateArgumentList JustTemplArgs( - Template, CanonicalDeducedArgumentList->asArray(), - /*Final=*/false); - if (addInstantiatedParametersToScope(nullptr, FD, Scope, JustTemplArgs)) - return TemplateDeductionResult::MiscellaneousDeductionFailure; - - if (FunctionTemplateDecl *FromMemTempl = - Template->getInstantiatedFromMemberTemplate()) { - while (FromMemTempl->getInstantiatedFromMemberTemplate()) - FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate(); - if (addInstantiatedParametersToScope( - nullptr, FromMemTempl->getTemplatedDecl(), Scope, MLTAL)) - return TemplateDeductionResult::MiscellaneousDeductionFailure; - } - - Qualifiers ThisQuals; - CXXRecordDecl *Record = nullptr; - if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) { - ThisQuals = Method->getMethodQualifiers(); - Record = Method->getParent(); - } - CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); - llvm::SmallVector<Expr *, 1> Converted; - if (CheckConstraintSatisfaction(Template, TemplateAC, MLTAL, - Info.getLocation(), - Info.AssociatedConstraintsSatisfaction)) - return TemplateDeductionResult::MiscellaneousDeductionFailure; - if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { - Info.reset(TemplateArgumentList::CreateCopy(Context, SugaredBuilder), - Info.takeCanonical()); - return TemplateDeductionResult::ConstraintsNotSatisfied; - } + bool NeedConstraintChecking = + !PartialOverloading || + CanonicalBuilder.size() == + FunctionTemplate->getTemplateParameters()->size(); + bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD); +#if 1 + if (!IsLambda && NeedConstraintChecking) { + if (CheckFunctionConstraintsWithoutInstantiation( + Info.getLocation(), FunctionTemplate->getCanonicalDecl(), + CanonicalBuilder, Info.AssociatedConstraintsSatisfaction)) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { + Info.reset(Info.takeSugared(), + TemplateArgumentList::CreateCopy(Context, CanonicalBuilder)); + return TemplateDeductionResult::ConstraintsNotSatisfied; } } - +#endif // C++ [temp.deduct.call]p10: [DR1391] // If deduction succeeds for all parameters that contain // template-parameters that participate in template argument deduction, @@ -3975,9 +3931,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( // ([temp.constr.decl]), those constraints are checked for satisfaction // ([temp.constr.constr]). If the constraints are not satisfied, type // deduction fails. - if (!PartialOverloading || - (CanonicalBuilder.size() == - FunctionTemplate->getTemplateParameters()->size())) { + if (IsLambda && NeedConstraintChecking) { if (CheckInstantiatedFunctionTemplateConstraints( Info.getLocation(), Specialization, CanonicalBuilder, Info.AssociatedConstraintsSatisfaction)) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 5a7cb294a1c86d..86a916cc3fc373 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -34,6 +34,7 @@ #include "clang/Sema/SemaSwift.h" #include "clang/Sema/Template.h" #include "clang/Sema/TemplateInstCallback.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/TimeProfiler.h" #include <optional> @@ -2136,7 +2137,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( return nullptr; QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo); - if (TemplateParams && TemplateParams->size()) { + if (false && TemplateParams && TemplateParams->size()) { auto *LastParam = dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back()); if (LastParam && LastParam->isImplicit() && @@ -2548,7 +2549,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( return nullptr; QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo); - if (TemplateParams && TemplateParams->size()) { + if (false && TemplateParams && TemplateParams->size()) { auto *LastParam = dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back()); if (LastParam && LastParam->isImplicit() && @@ -4577,17 +4578,17 @@ void Sema::addInstantiatedLocalVarsToScope(FunctionDecl *Function, } } -static bool addInstantiatedParametersToScope( - Sema &SemaRef, MutableArrayRef<ParmVarDecl *> InstantiatedParamDecls, - const FunctionDecl *PatternDecl, LocalInstantiationScope &Scope, +bool Sema::addInstantiatedParametersToScope( + FunctionDecl *Function, const FunctionDecl *PatternDecl, + LocalInstantiationScope &Scope, const MultiLevelTemplateArgumentList &TemplateArgs) { unsigned FParamIdx = 0; for (unsigned I = 0, N = PatternDecl->getNumParams(); I != N; ++I) { const ParmVarDecl *PatternParam = PatternDecl->getParamDecl(I); if (!PatternParam->isParameterPack()) { // Simple case: not a parameter pack. - assert(FParamIdx < InstantiatedParamDecls.size()); - ParmVarDecl *FunctionParam = InstantiatedParamDecls[FParamIdx]; + assert(FParamIdx < Function->getNumParams()); + ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); FunctionParam->setDeclName(PatternParam->getDeclName()); // If the parameter's type is not dependent, update it to match the type // in the pattern. They can differ in top-level cv-qualifiers, and we want @@ -4596,9 +4597,9 @@ static bool addInstantiatedParametersToScope( // it's instantiation-dependent. // FIXME: Updating the type to work around this is at best fragile. if (!PatternDecl->getType()->isDependentType()) { - QualType T = SemaRef.SubstType(PatternParam->getType(), TemplateArgs, - FunctionParam->getLocation(), - FunctionParam->getDeclName()); + QualType T = SubstType(PatternParam->getType(), TemplateArgs, + FunctionParam->getLocation(), + FunctionParam->getDeclName()); if (T.isNull()) return true; FunctionParam->setType(T); @@ -4612,19 +4613,18 @@ static bool addInstantiatedParametersToScope( // Expand the parameter pack. Scope.MakeInstantiatedLocalArgPack(PatternParam); std::optional<unsigned> NumArgumentsInExpansion = - SemaRef.getNumArgumentsInExpansion(PatternParam->getType(), - TemplateArgs); + getNumArgumentsInExpansion(PatternParam->getType(), TemplateArgs); if (NumArgumentsInExpansion) { QualType PatternType = PatternParam->getType()->castAs<PackExpansionType>()->getPattern(); for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) { - ParmVarDecl *FunctionParam = InstantiatedParamDecls[FParamIdx]; + ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); FunctionParam->setDeclName(PatternParam->getDeclName()); if (!PatternDecl->getType()->isDependentType()) { - Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, Arg); - QualType T = SemaRef.SubstType(PatternType, TemplateArgs, - FunctionParam->getLocation(), - FunctionParam->getDeclName()); + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(*this, Arg); + QualType T = + SubstType(PatternType, TemplateArgs, FunctionParam->getLocation(), + FunctionParam->getDeclName()); if (T.isNull()) return true; FunctionParam->setType(T); @@ -4639,25 +4639,6 @@ static bool addInstantiatedParametersToScope( return false; } -bool Sema::addInstantiatedParametersToScope( - FunctionDecl *Function, const FunctionDecl *PatternDecl, - LocalInstantiationScope &Scope, - const MultiLevelTemplateArgumentList &TemplateArgs) { - if (Function) - return ::addInstantiatedParametersToScope(*this, Function->parameters(), - PatternDecl, Scope, TemplateArgs); - FunctionTypeLoc TypeLoc = PatternDecl->getFunctionTypeLoc(); - assert(!TypeLoc.isNull() && "Invalid function TypeLoc?"); - SmallVector<QualType> ParamTypes; - SmallVector<ParmVarDecl *> OutParams; - Sema::ExtParameterInfoBuilder ExtParamInfos; - if (SubstParmTypes(PatternDecl->getLocation(), TypeLoc.getParams(), nullptr, - TemplateArgs, ParamTypes, &OutParams, ExtParamInfos)) - return true; - return ::addInstantiatedParametersToScope(*this, OutParams, PatternDecl, - Scope, TemplateArgs); -} - bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD, ParmVarDecl *Param) { assert(Param->hasUninstantiatedDefaultArg()); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 62287c2d26375c..5a9fc5ba6bc127 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -713,7 +713,7 @@ class TreeTransform { /// variables vector are acceptable. /// /// LastParamTransformed, if non-null, will be set to the index of the last - /// parameter on which transfromation was started. In the event of an error, + /// parameter on which transformation was started. In the event of an error, /// this will contain the parameter which failed to instantiate. /// /// Return true on error. diff --git a/libcxx/include/__chrono/leap_second.h b/libcxx/include/__chrono/leap_second.h index 1a0e7f3107de81..4e7f208acd3409 100644 --- a/libcxx/include/__chrono/leap_second.h +++ b/libcxx/include/__chrono/leap_second.h @@ -105,7 +105,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const sys_time<_Duration>& __x, return !(__x < __y); } -# ifndef _LIBCPP_COMPILER_GCC +# if 0 // This requirement cause a compilation loop in GCC-13 and running out of memory. // TODO TZDB Test whether GCC-14 fixes this. template <class _Duration> >From 631be750f4d80b766608457da7334eddddbd788b Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 17 Aug 2024 22:27:50 +0800 Subject: [PATCH 06/13] Remove debugging logs --- clang/lib/Sema/SemaConcept.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index ad17d714037ed0..7c61606e5d83f5 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -461,17 +461,13 @@ static ExprResult calculateConstraintSatisfaction( return ExprError(); llvm::FoldingSetNodeID ID; - // llvm::errs() << "Preparing for checking " << Template << "\n"; if (Template && DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) { - // Template->dump(); Satisfaction.IsSatisfied = false; Satisfaction.ContainsErrors = true; - // __builtin_trap(); return ExprEmpty(); } - // llvm::errs() << "Pushing " << Template << "\n"; SatisfactionStackRAII StackRAII(S, Template, ID); // We do not want error diagnostics escaping here. >From 7d484e22555c46de9c820001311763a5df9ffb45 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 17 Aug 2024 22:30:09 +0800 Subject: [PATCH 07/13] Revert unrelated changes --- clang/include/clang/Sema/Sema.h | 3 +-- clang/lib/Sema/SemaTemplateInstantiate.cpp | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 33eff47a8707bf..0c94ebfbe91c32 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13064,8 +13064,7 @@ class Sema final : public SemaBase { std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt, bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, bool ForConstraintInstantiation = false, - bool SkipForSpecialization = false, - MultiLevelTemplateArgumentList *Merged = nullptr); + bool SkipForSpecialization = false); /// RAII object to handle the state changes required to synthesize /// a function body. diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 6a6ea8849badc8..efae547b21c715 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -468,11 +468,10 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( const NamedDecl *ND, const DeclContext *DC, bool Final, std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary, const FunctionDecl *Pattern, bool ForConstraintInstantiation, - bool SkipForSpecialization, MultiLevelTemplateArgumentList *Merged) { + bool SkipForSpecialization) { assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); // Accumulate the set of template argument lists in this structure. - MultiLevelTemplateArgumentList Ret; - MultiLevelTemplateArgumentList &Result = Merged ? *Merged : Ret; + MultiLevelTemplateArgumentList Result; using namespace TemplateInstArgsHelpers; const Decl *CurDecl = ND; >From c5154079b9c41e841d0225fc4b2d77f5f6e7b2ea Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 17 Aug 2024 23:50:42 +0800 Subject: [PATCH 08/13] Fix tests --- clang/lib/Sema/SemaConcept.cpp | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 7c61606e5d83f5..a4cfd02caa014e 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1132,15 +1132,17 @@ class InstantiateReferencedParameter : public TreeTransform<InstantiateReferencedParameter> { const MultiLevelTemplateArgumentList &TemplateArgs; - DeclContext *FunctionDC; + llvm::SmallPtrSet<ParmVarDecl *, 4> InstantiatedDecls; + + FunctionDecl *PrimaryTemplatedFunction; using inherited = TreeTransform<InstantiateReferencedParameter>; bool instantiateParameterToScope(ParmVarDecl *OldParm, LocalInstantiationScope &Scope) { - // Current context might have been changed by lambda expressions. So resume - // it before we substitute parameters. - Sema::ContextRAII Context(SemaRef, FunctionDC); + // The current context might have been changed by lambda expressions. So + // resume it before we substitute into parameters. + Sema::ContextRAII Context(SemaRef, PrimaryTemplatedFunction); std::optional<unsigned> NumExpansions; ParmVarDecl *NewParm = nullptr; unsigned IndexAdjustment = 0; @@ -1194,13 +1196,17 @@ class InstantiateReferencedParameter public: InstantiateReferencedParameter( Sema &SemaRef, const MultiLevelTemplateArgumentList &TemplateArgs, - DeclContext *FunctionDC) - : inherited(SemaRef), TemplateArgs(TemplateArgs), FunctionDC(FunctionDC) { - } + FunctionDecl *PrimaryTemplatedFunction) + : inherited(SemaRef), TemplateArgs(TemplateArgs), + PrimaryTemplatedFunction(PrimaryTemplatedFunction) {} Decl *TransformDecl(SourceLocation Loc, Decl *D) { - if (auto *PVD = dyn_cast<ParmVarDecl>(D)) + if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(D); + PVD && PVD->getDeclContext() == PrimaryTemplatedFunction && + !InstantiatedDecls.contains(PVD)) { instantiateParameterToScope(PVD, *SemaRef.CurrentInstantiationScope); + InstantiatedDecls.insert(PVD); + } return D; } @@ -1241,7 +1247,16 @@ bool Sema::CheckFunctionConstraintsWithoutInstantiation( /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); - InstantiateReferencedParameter(*this, MLTAL, FD) + FunctionDecl *PatternDecl = FD; + + if (FunctionTemplateDecl *FromMemTempl = + Template->getInstantiatedFromMemberTemplate()) { + while (FromMemTempl->getInstantiatedFromMemberTemplate()) + FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate(); + PatternDecl = FromMemTempl->getTemplatedDecl(); + } + + InstantiateReferencedParameter(*this, MLTAL, PatternDecl) .TraverseConstraintExprs(TemplateAC); Qualifiers ThisQuals; >From a9da5a3e53e0900aa60addb4eaefc213a96836cc Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sun, 18 Aug 2024 00:30:32 +0800 Subject: [PATCH 09/13] Fix libcxx tests --- libcxx/include/__chrono/leap_second.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcxx/include/__chrono/leap_second.h b/libcxx/include/__chrono/leap_second.h index 4e7f208acd3409..a439f3f5c6cc62 100644 --- a/libcxx/include/__chrono/leap_second.h +++ b/libcxx/include/__chrono/leap_second.h @@ -105,11 +105,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const sys_time<_Duration>& __x, return !(__x < __y); } -# if 0 +# ifndef _LIBCPP_COMPILER_GCC // This requirement cause a compilation loop in GCC-13 and running out of memory. // TODO TZDB Test whether GCC-14 fixes this. template <class _Duration> - requires three_way_comparable_with<sys_seconds, sys_time<_Duration>> + requires(same_as<seconds, _Duration> || three_way_comparable_with<sys_seconds, sys_time<_Duration>>) _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const leap_second& __x, const sys_time<_Duration>& __y) { return __x.date() <=> __y; } >From cebe7063174a12dfedbe28185b7173b8826093ab Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sun, 18 Aug 2024 18:41:08 +0800 Subject: [PATCH 10/13] Instantiate the decls as we substituting constraints --- clang/include/clang/Sema/Sema.h | 4 +- clang/include/clang/Sema/Template.h | 3 + clang/lib/Sema/SemaConcept.cpp | 126 +++----------------- clang/lib/Sema/SemaTemplateInstantiate.cpp | 129 +++++++++++++++++++-- 4 files changed, 141 insertions(+), 121 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0c94ebfbe91c32..0f186ac6aecccb 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13329,7 +13329,9 @@ class Sema final : public SemaBase { // Must be used instead of SubstExpr at 'constraint checking' time. ExprResult SubstConstraintExpr(Expr *E, - const MultiLevelTemplateArgumentList &TemplateArgs); + const MultiLevelTemplateArgumentList &TemplateArgs, + DeclContext *InstantiatingFunctionDC = nullptr); + // Unlike the above, this does not evaluates constraints. ExprResult SubstConstraintExprWithoutSatisfaction( Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index 0340c23fd170d6..5de4bf0148652c 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -515,6 +515,9 @@ enum class TemplateSubstitutionKind : char { llvm::PointerUnion<Decl *, DeclArgumentPack *> * findInstantiationOf(const Decl *D); + llvm::PointerUnion<Decl *, DeclArgumentPack *> * + findInstantiationUnsafe(const Decl *D); + void InstantiatedLocal(const Decl *D, Decl *Inst); void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst); void MakeInstantiatedLocalArgPack(const Decl *D); diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index a4cfd02caa014e..99c1b4a6998544 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -470,10 +470,27 @@ static ExprResult calculateConstraintSatisfaction( SatisfactionStackRAII StackRAII(S, Template, ID); + DeclContext *FunctionDC = nullptr; + if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(Template)) { + FunctionDecl *FD = FTD->getTemplatedDecl(); + FunctionDC = FD; + // The function has been instantiated. Don't bother to instantiate its + // parameters then. + if (FD->getTemplateInstantiationPattern(/*ForDefinition=*/false)) + FunctionDC = nullptr; + else if (FunctionTemplateDecl *FromMemTempl = + FTD->getInstantiatedFromMemberTemplate()) { + while (FromMemTempl->getInstantiatedFromMemberTemplate()) + FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate(); + FunctionDC = FromMemTempl->getTemplatedDecl(); + } + } + // We do not want error diagnostics escaping here. Sema::SFINAETrap Trap(S); SubstitutedExpression = - S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL); + S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL, + /*InstantiatingFunctionDC=*/FunctionDC); if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) { // C++2a [temp.constr.atomic]p1 @@ -1122,102 +1139,6 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( PointOfInstantiation, Satisfaction); } -namespace { - -// We employ a TreeTransform because RAV couldn't recurse into a bunch of -// Exprs e.g. SizeOfPackExpr, CXXFoldExpr, etc. -// FIXME: Could we do the Decl instantiation as we substitute into -// the constraint expressions? -class InstantiateReferencedParameter - : public TreeTransform<InstantiateReferencedParameter> { - const MultiLevelTemplateArgumentList &TemplateArgs; - - llvm::SmallPtrSet<ParmVarDecl *, 4> InstantiatedDecls; - - FunctionDecl *PrimaryTemplatedFunction; - - using inherited = TreeTransform<InstantiateReferencedParameter>; - - bool instantiateParameterToScope(ParmVarDecl *OldParm, - LocalInstantiationScope &Scope) { - // The current context might have been changed by lambda expressions. So - // resume it before we substitute into parameters. - Sema::ContextRAII Context(SemaRef, PrimaryTemplatedFunction); - std::optional<unsigned> NumExpansions; - ParmVarDecl *NewParm = nullptr; - unsigned IndexAdjustment = 0; - if (OldParm->isParameterPack()) { - SmallVector<UnexpandedParameterPack, 2> Unexpanded; - TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc(); - PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>(); - TypeLoc Pattern = ExpansionTL.getPatternLoc(); - SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); - - assert(!Unexpanded.empty() && - "A pack Decl doesn't contain anything unexpanded?"); - - bool ShouldExpand = false; - bool RetainExpansion = false; - std::optional<unsigned> OrigNumExpansions = - ExpansionTL.getTypePtr()->getNumExpansions(); - NumExpansions = OrigNumExpansions; - if (SemaRef.CheckParameterPacksForExpansion( - ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(), - Unexpanded, TemplateArgs, ShouldExpand, RetainExpansion, - NumExpansions)) - return true; - - assert(ShouldExpand && !RetainExpansion && - "Shouldn't retain an expansion here!"); - Scope.MakeInstantiatedLocalArgPack(OldParm); - - for (unsigned I = 0; I != *NumExpansions; ++I) { - Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I); - ParmVarDecl *NewParm = SemaRef.SubstParmVarDecl( - OldParm, TemplateArgs, /*indexAdjustment=*/IndexAdjustment++, - NumExpansions, /*ExpectParameterPack=*/false, - /*EvaluateConstraints=*/false); - if (!NewParm) - return true; - } - - return false; - } - NewParm = SemaRef.SubstParmVarDecl(OldParm, TemplateArgs, - /*indexAdjustment=*/IndexAdjustment, - std::nullopt, - /*ExpectParameterPack=*/false); - if (!NewParm) - return true; - Scope.InstantiatedLocal(OldParm, NewParm); - return false; - } - -public: - InstantiateReferencedParameter( - Sema &SemaRef, const MultiLevelTemplateArgumentList &TemplateArgs, - FunctionDecl *PrimaryTemplatedFunction) - : inherited(SemaRef), TemplateArgs(TemplateArgs), - PrimaryTemplatedFunction(PrimaryTemplatedFunction) {} - - Decl *TransformDecl(SourceLocation Loc, Decl *D) { - if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(D); - PVD && PVD->getDeclContext() == PrimaryTemplatedFunction && - !InstantiatedDecls.contains(PVD)) { - instantiateParameterToScope(PVD, *SemaRef.CurrentInstantiationScope); - InstantiatedDecls.insert(PVD); - } - return D; - } - - void TraverseConstraintExprs(ArrayRef<const Expr *> Exprs) { - for (auto *E : Exprs) - TransformExpr(const_cast<Expr *>(E)); - } -}; - -} // namespace - bool Sema::CheckFunctionConstraintsWithoutInstantiation( SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs, @@ -1247,17 +1168,6 @@ bool Sema::CheckFunctionConstraintsWithoutInstantiation( /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); - FunctionDecl *PatternDecl = FD; - - if (FunctionTemplateDecl *FromMemTempl = - Template->getInstantiatedFromMemberTemplate()) { - while (FromMemTempl->getInstantiatedFromMemberTemplate()) - FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate(); - PatternDecl = FromMemTempl->getTemplatedDecl(); - } - - InstantiateReferencedParameter(*this, MLTAL, PatternDecl) - .TraverseConstraintExprs(TemplateAC); Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index efae547b21c715..4f590fc1afc5b4 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1345,6 +1345,16 @@ namespace { // Whether to evaluate the C++20 constraints or simply substitute into them. bool EvaluateConstraints = true; + // Whether to instantiate function parameters in one transformation. + // The DeclContext is the context where the instantiated parameter would + // belong. + DeclContext *FunctionDCForParameterDeclInstantiation; + + LocalInstantiationScope *ParameterInstantiationScope; + private: + bool instantiateParameterToScope(ParmVarDecl *OldParm, + LocalInstantiationScope &Scope); + public: typedef TreeTransform<TemplateInstantiator> inherited; @@ -1357,10 +1367,18 @@ namespace { void setEvaluateConstraints(bool B) { EvaluateConstraints = B; } - bool getEvaluateConstraints() { + bool getEvaluateConstraints() const { return EvaluateConstraints; } + void setInstantiatingFunctionDC(DeclContext *FunctionDC) { + FunctionDCForParameterDeclInstantiation = FunctionDC; + } + + void setParameterInstantiationScope(LocalInstantiationScope *Scope) { + ParameterInstantiationScope = Scope; + } + /// Determine whether the given type \p T has already been /// transformed. /// @@ -1397,12 +1415,25 @@ namespace { ArrayRef<UnexpandedParameterPack> Unexpanded, bool &ShouldExpand, bool &RetainExpansion, std::optional<unsigned> &NumExpansions) { - return getSema().CheckParameterPacksForExpansion(EllipsisLoc, - PatternRange, Unexpanded, - TemplateArgs, - ShouldExpand, - RetainExpansion, - NumExpansions); + if (ParameterInstantiationScope) { + for (UnexpandedParameterPack ParmPack : Unexpanded) { + NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>(); + if (!VD) + continue; + if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(VD); + PVD && + PVD->getDeclContext() == + FunctionDCForParameterDeclInstantiation && + !ParameterInstantiationScope->findInstantiationUnsafe(PVD)) { + // Fall through to the default lookup even if we have failed to + // instantiate anything. We're likely to crash thereafter. + instantiateParameterToScope(PVD, *ParameterInstantiationScope); + } + } + } + return getSema().CheckParameterPacksForExpansion( + EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand, + RetainExpansion, NumExpansions); } void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { @@ -1836,9 +1867,68 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { // template parameter. } + if (auto *PVD = dyn_cast<ParmVarDecl>(D); + PVD && PVD->getDeclContext() == FunctionDCForParameterDeclInstantiation && + !ParameterInstantiationScope->findInstantiationUnsafe(PVD)) { + // Fall through to the default lookup even if we have failed to instantiate + // anything. We're likely to crash thereafter. + instantiateParameterToScope(PVD, *ParameterInstantiationScope); + } + return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs); } +bool TemplateInstantiator::instantiateParameterToScope( + ParmVarDecl *OldParm, LocalInstantiationScope &Scope) { + // The current context / instantiation scope might have been changed by lambda + // expressions. So resume them before we substitute into parameters. + llvm::SaveAndRestore CurrentScope(SemaRef.CurrentInstantiationScope, &Scope); + Sema::ContextRAII Context(SemaRef, FunctionDCForParameterDeclInstantiation); + std::optional<unsigned> NumExpansions; + unsigned IndexAdjustment = 0; + if (OldParm->isParameterPack()) { + SmallVector<UnexpandedParameterPack, 2> Unexpanded; + TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc(); + PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>(); + TypeLoc Pattern = ExpansionTL.getPatternLoc(); + SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); + + assert(!Unexpanded.empty() && + "A pack Decl doesn't contain anything unexpanded?"); + + bool ShouldExpand = false; + bool RetainExpansion = false; + std::optional<unsigned> OrigNumExpansions = + ExpansionTL.getTypePtr()->getNumExpansions(); + NumExpansions = OrigNumExpansions; + if (SemaRef.CheckParameterPacksForExpansion( + ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(), Unexpanded, + TemplateArgs, ShouldExpand, RetainExpansion, NumExpansions)) + return true; + + assert(ShouldExpand && !RetainExpansion && + "Shouldn't retain an expansion here!"); + Scope.MakeInstantiatedLocalArgPack(OldParm); + + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I); + ParmVarDecl *NewParm = SemaRef.SubstParmVarDecl( + OldParm, TemplateArgs, /*indexAdjustment=*/IndexAdjustment++, + NumExpansions, /*ExpectParameterPack=*/false, + /*EvaluateConstraints=*/false); + if (!NewParm) + return true; + } + + return false; + } + ParmVarDecl *NewParm = SemaRef.SubstParmVarDecl( + OldParm, TemplateArgs, + /*indexAdjustment=*/IndexAdjustment, std::nullopt, + /*ExpectParameterPack=*/false); + return !NewParm; +} + Decl *TemplateInstantiator::TransformDefinition(SourceLocation Loc, Decl *D) { Decl *Inst = getSema().SubstDecl(D, getSema().CurContext, TemplateArgs); if (!Inst) @@ -4259,10 +4349,17 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) { ExprResult Sema::SubstConstraintExpr(Expr *E, - const MultiLevelTemplateArgumentList &TemplateArgs) { - // FIXME: should call SubstExpr directly if this function is equivalent or - // should it be different? - return SubstExpr(E, TemplateArgs); + const MultiLevelTemplateArgumentList &TemplateArgs, + DeclContext *InstantiatingFunctionDC) { + if (!E) + return E; + + TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(), + DeclarationName()); + Instantiator.setInstantiatingFunctionDC(InstantiatingFunctionDC); + Instantiator.setParameterInstantiationScope(CurrentInstantiationScope); + + return Instantiator.TransformExpr(E); } ExprResult Sema::SubstConstraintExprWithoutSatisfaction( @@ -4346,7 +4443,7 @@ static const Decl *getCanonicalParmVarDecl(const Decl *D) { llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> * -LocalInstantiationScope::findInstantiationOf(const Decl *D) { +LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) { D = getCanonicalParmVarDecl(D); for (LocalInstantiationScope *Current = this; Current; Current = Current->Outer) { @@ -4371,6 +4468,14 @@ LocalInstantiationScope::findInstantiationOf(const Decl *D) { break; } + return nullptr; +} + +llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> * +LocalInstantiationScope::findInstantiationOf(const Decl *D) { + auto *Result = findInstantiationUnsafe(D); + if (Result) + return Result; // If we're performing a partial substitution during template argument // deduction, we may not have values for template parameters yet. if (isa<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) || >From e0b21a546664b1c8324fb0bfe6ec871acb564a1a Mon Sep 17 00:00:00 2001 From: Mark de Wever <ko...@xs4all.nl> Date: Sun, 18 Aug 2024 17:09:40 +0200 Subject: [PATCH 11/13] [libc++][chono] Use hidden friends for leap_second comparison. The function template<class Duration> requires three_way_comparable_with<sys_seconds, sys_time<Duration>> constexpr auto operator<=>(const leap_second& x, const sys_time<Duration>& y) noexcept; Has a recursive constrained. This caused an infinite loop in GCC and is now hit by https://github.com/llvm/llvm-project/pull/102857. A fix would be to make this function a hidden friend, this solution is propsed in LWG4139. For consistency all comparisons are made hidden friends. Since the issue causes compilation failures no additional test are needed. --- libcxx/docs/Status/Cxx2cIssues.csv | 4 +- libcxx/include/__chrono/leap_second.h | 131 +++++++++++++------------- 2 files changed, 70 insertions(+), 65 deletions(-) diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index a92a1fef779800..82590d25336808 100644 --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -76,6 +76,6 @@ "`LWG4105 <https://wg21.link/LWG4105>`__","``ranges::ends_with``\`s Returns misses difference casting","St. Louis June 2024","","","|ranges|" "`LWG4106 <https://wg21.link/LWG4106>`__","``basic_format_args`` should not be default-constructible","St. Louis June 2024","|Complete|","19.0","|format|" "","","","","","" -"`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Yet Adopted","|Complete|","16.0","" -"XXXX","The sys_info range should be affected by save","Not Yet Adopted","|Complete|","19.0","" +"`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Adopted Yet","|Complete|","16.0","" +"`LWG4139 <https://wg21.link/LWG4139>`__","ยง[time.zone.leap] recursive constraint in <=>","Not Adopted Yet","|Complete|","20.0","" "","","","","","" diff --git a/libcxx/include/__chrono/leap_second.h b/libcxx/include/__chrono/leap_second.h index a439f3f5c6cc62..d79111ed8eecfc 100644 --- a/libcxx/include/__chrono/leap_second.h +++ b/libcxx/include/__chrono/leap_second.h @@ -50,70 +50,75 @@ class leap_second { private: sys_seconds __date_; seconds __value_; -}; -_LIBCPP_HIDE_FROM_ABI inline constexpr bool operator==(const leap_second& __x, const leap_second& __y) { - return __x.date() == __y.date(); -} - -_LIBCPP_HIDE_FROM_ABI inline constexpr strong_ordering operator<=>(const leap_second& __x, const leap_second& __y) { - return __x.date() <=> __y.date(); -} - -template <class _Duration> -_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const leap_second& __x, const sys_time<_Duration>& __y) { - return __x.date() == __y; -} - -template <class _Duration> -_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const leap_second& __x, const sys_time<_Duration>& __y) { - return __x.date() < __y; -} - -template <class _Duration> -_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const sys_time<_Duration>& __x, const leap_second& __y) { - return __x < __y.date(); -} - -template <class _Duration> -_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const leap_second& __x, const sys_time<_Duration>& __y) { - return __y < __x; -} - -template <class _Duration> -_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const sys_time<_Duration>& __x, const leap_second& __y) { - return __y < __x; -} - -template <class _Duration> -_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const leap_second& __x, const sys_time<_Duration>& __y) { - return !(__y < __x); -} - -template <class _Duration> -_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const sys_time<_Duration>& __x, const leap_second& __y) { - return !(__y < __x); -} - -template <class _Duration> -_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const leap_second& __x, const sys_time<_Duration>& __y) { - return !(__x < __y); -} - -template <class _Duration> -_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const sys_time<_Duration>& __x, const leap_second& __y) { - return !(__x < __y); -} - -# ifndef _LIBCPP_COMPILER_GCC -// This requirement cause a compilation loop in GCC-13 and running out of memory. -// TODO TZDB Test whether GCC-14 fixes this. -template <class _Duration> - requires(same_as<seconds, _Duration> || three_way_comparable_with<sys_seconds, sys_time<_Duration>>) -_LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const leap_second& __x, const sys_time<_Duration>& __y) { - return __x.date() <=> __y; -} -# endif + // The function + // template<class Duration> + // requires three_way_comparable_with<sys_seconds, sys_time<Duration>> + // constexpr auto operator<=>(const leap_second& x, const sys_time<Duration>& y) noexcept; + // + // Has constraints that are recursive (LWG4139). The proposed resolution is + // to make the funcion a hidden friend. For consistency make this change for + // all comparison functions. + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const leap_second& __x, const leap_second& __y) { + return __x.date() == __y.date(); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering operator<=>(const leap_second& __x, const leap_second& __y) { + return __x.date() <=> __y.date(); + } + + template <class _Duration> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const leap_second& __x, const sys_time<_Duration>& __y) { + return __x.date() == __y; + } + + template <class _Duration> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const leap_second& __x, const sys_time<_Duration>& __y) { + return __x.date() < __y; + } + + template <class _Duration> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const sys_time<_Duration>& __x, const leap_second& __y) { + return __x < __y.date(); + } + + template <class _Duration> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const leap_second& __x, const sys_time<_Duration>& __y) { + return __y < __x; + } + + template <class _Duration> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const sys_time<_Duration>& __x, const leap_second& __y) { + return __y < __x; + } + + template <class _Duration> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const leap_second& __x, const sys_time<_Duration>& __y) { + return !(__y < __x); + } + + template <class _Duration> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const sys_time<_Duration>& __x, const leap_second& __y) { + return !(__y < __x); + } + + template <class _Duration> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const leap_second& __x, const sys_time<_Duration>& __y) { + return !(__x < __y); + } + + template <class _Duration> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const sys_time<_Duration>& __x, const leap_second& __y) { + return !(__x < __y); + } + + template <class _Duration> + requires three_way_comparable_with<sys_seconds, sys_time<_Duration>> + _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const leap_second& __x, const sys_time<_Duration>& __y) { + return __x.date() <=> __y; + } +}; } // namespace chrono >From 30dc1d1b00fa6ae8c3fe1ddac4fde52253ec3cc2 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Mon, 19 Aug 2024 09:53:37 +0800 Subject: [PATCH 12/13] clang-format --- clang/lib/Sema/SemaConcept.cpp | 1 - clang/lib/Sema/SemaTemplateInstantiate.cpp | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 99c1b4a6998544..f89dd6878b9695 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1168,7 +1168,6 @@ bool Sema::CheckFunctionConstraintsWithoutInstantiation( /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); - Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 4f590fc1afc5b4..971dc65eb193cc 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1351,6 +1351,7 @@ namespace { DeclContext *FunctionDCForParameterDeclInstantiation; LocalInstantiationScope *ParameterInstantiationScope; + private: bool instantiateParameterToScope(ParmVarDecl *OldParm, LocalInstantiationScope &Scope); @@ -1367,9 +1368,7 @@ namespace { void setEvaluateConstraints(bool B) { EvaluateConstraints = B; } - bool getEvaluateConstraints() const { - return EvaluateConstraints; - } + bool getEvaluateConstraints() const { return EvaluateConstraints; } void setInstantiatingFunctionDC(DeclContext *FunctionDC) { FunctionDCForParameterDeclInstantiation = FunctionDC; @@ -4441,7 +4440,6 @@ static const Decl *getCanonicalParmVarDecl(const Decl *D) { return D; } - llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> * LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) { D = getCanonicalParmVarDecl(D); >From 6a5085aece0dcf641cac863fd72b7b107f908def Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Wed, 28 Aug 2024 17:44:22 +0800 Subject: [PATCH 13/13] Quote the standard wordings, update the DR status page --- clang/lib/Sema/SemaConcept.cpp | 3 +++ clang/lib/Sema/SemaTemplateDeduction.cpp | 11 ++++++----- clang/lib/Sema/SemaTemplateInstantiate.cpp | 3 +++ clang/test/CXX/drs/cwg23xx.cpp | 2 +- clang/test/CXX/drs/cwg27xx.cpp | 20 ++++++++++++++++++++ clang/www/cxx_dr_status.html | 2 +- 6 files changed, 34 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 17e6aae20638b5..9f21762c97a87c 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -470,6 +470,9 @@ static ExprResult calculateConstraintSatisfaction( SatisfactionStackRAII StackRAII(S, Template, ID); + // [CWG2770] Function parameters should be instantiated when they are + // needed by a satisfaction check of an atomic constraint or + // (recursively) by another function parameter. DeclContext *FunctionDC = nullptr; if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(Template)) { FunctionDecl *FD = FTD->getTemplatedDecl(); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index a1a109ac8bc9a4..177f1fc24a2524 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3865,15 +3865,17 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( Owner = FD->getLexicalDeclContext(); } - // [DR2369] - // FIXME: We have to partially instantiate lambda's captures for constraint - // evaluation. + // C++20 [temp.deduct.general]p5: [DR2369] + // If the function template has associated constraints, those constraints are + // checked for satisfaction. If the constraints are not satisfied, type + // deduction fails. bool NeedConstraintChecking = !PartialOverloading || CanonicalBuilder.size() == FunctionTemplate->getTemplateParameters()->size(); + // FIXME: We haven't implemented DR2369 for lambdas yet, because we need + // the captured variables to be instantiated in the scope. bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD); -#if 1 if (!IsLambda && NeedConstraintChecking) { if (CheckFunctionConstraintsWithoutInstantiation( Info.getLocation(), FunctionTemplate->getCanonicalDecl(), @@ -3885,7 +3887,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( return TemplateDeductionResult::ConstraintsNotSatisfied; } } -#endif // C++ [temp.deduct.call]p10: [DR1391] // If deduction succeeds for all parameters that contain // template-parameters that participate in template argument deduction, diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 6f267218bbbaba..cd94bcc099e539 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1927,6 +1927,9 @@ bool TemplateInstantiator::instantiateParameterToScope( return false; } + // FIXME: The index of the instantiated parameter might be wrong because we + // don't know the offset of the prior parameters. But it doesn't matter + // regarding constraint evaluation. ParmVarDecl *NewParm = SemaRef.SubstParmVarDecl( OldParm, TemplateArgs, /*indexAdjustment=*/IndexAdjustment, std::nullopt, diff --git a/clang/test/CXX/drs/cwg23xx.cpp b/clang/test/CXX/drs/cwg23xx.cpp index b9585e159c0fb9..f4992c1426844a 100644 --- a/clang/test/CXX/drs/cwg23xx.cpp +++ b/clang/test/CXX/drs/cwg23xx.cpp @@ -531,7 +531,7 @@ namespace cwg2397 { // cwg2397: 17 #if __cplusplus >= 202002L -namespace cwg2369 { // cwg2369: 20 +namespace cwg2369 { // cwg2369: partial template <class T> struct Z { typedef typename T::x xx; diff --git a/clang/test/CXX/drs/cwg27xx.cpp b/clang/test/CXX/drs/cwg27xx.cpp index 406c8ea41f3b2f..1bd86363097432 100644 --- a/clang/test/CXX/drs/cwg27xx.cpp +++ b/clang/test/CXX/drs/cwg27xx.cpp @@ -101,6 +101,26 @@ static_assert(!__is_layout_compatible(StructWithAnonUnion, StructWithAnonUnion3) #endif } // namespace cwg2759 +#if __cplusplus >= 202002L +namespace cwg2770 { // cwg2770: 20 +template<typename T> +struct B { + static_assert(sizeof(T) == 1); + using type = int; +}; + +template<typename T> +int f(T t, typename B<T>::type u) requires (sizeof t == 1); + +template<typename T> +int f(T t, long); + +int i = f(1, 2); +int j = f('a', 2); + +} // namespace cwg2770 +#endif + namespace cwg2789 { // cwg2789: 18 #if __cplusplus >= 202302L template <typename T = int> diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 395b5d3bff49a6..3a797710312707 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -16436,7 +16436,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/2770.html">2770</a></td> <td>open</td> <td>Trailing <I>requires-clause</I> can refer to function parameters before they are substituted into</td> - <td align="center">Not resolved</td> + <td align="center">Clang 20</td> </tr> <tr id="2771"> <td><a href="https://cplusplus.github.io/CWG/issues/2771.html">2771</a></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits