[libcxx-commits] [clang] [libcxx] [Clang] Implement CWG2369 "Ordering between constraints and substitution" (PR #102857)
Younan Zhang via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Oct 8 22:25:05 PDT 2024
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 <zyn7109 at gmail.com>
Date: Mon, 12 Aug 2024 23:32:34 +0800
Subject: [PATCH 01/19] [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 <zyn7109 at gmail.com>
Date: Tue, 13 Aug 2024 09:41:13 +0800
Subject: [PATCH 02/19] 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 <zyn7109 at gmail.com>
Date: Sun, 11 Aug 2024 16:51:34 +0800
Subject: [PATCH 03/19] 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 <zyn7109 at gmail.com>
Date: Mon, 12 Aug 2024 15:22:42 +0800
Subject: [PATCH 04/19] 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 at -3 {{in instantiation of requirement here}}
// since-cxx20-note at -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 at -1 {{while checking constraint satisfaction for template 'bar<int>' required here}} \
- // expected-note at -1 {{in instantiation of function template specialization}}
+ // expected-note at -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 at +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 at +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 at -4{{while checking the satisfaction}}
// expected-note at -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 at -1 {{candidate template ignored: could not match}}
+// expected-note at -1 {{candidate template ignored: could not match}} expected-note at -1 {{candidate template ignored: constraints not satisfied}}
// expected-note at -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 at -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 at -1 3 {{in instantiation of}}
+ // expected-note at -1 3 {{while substituting deduced template arguments}}
// expected-error at -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 at -1 {{in instantiation of function template specialization 'lambda_in_constraints::func<int>'}}
+ // expected-note at -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 at -1{{candidate template ignored}}
int a = (foo<int>(), 0);
// expected-note at -1 {{while checking}} expected-error at -1{{no matching function}} \
- expected-note at -1 {{in instantiation}}
+ expected-note at -1 {{while substituting}}
template<typename T> void bar() requires requires { requires f<int[2]>; } { };
// expected-note at -1{{in instantiation}} \
expected-note at -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 at +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 <zyn7109 at gmail.com>
Date: Mon, 12 Aug 2024 23:34:33 +0800
Subject: [PATCH 05/19] 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 <zyn7109 at gmail.com>
Date: Sat, 17 Aug 2024 22:27:50 +0800
Subject: [PATCH 06/19] 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 <zyn7109 at gmail.com>
Date: Sat, 17 Aug 2024 22:30:09 +0800
Subject: [PATCH 07/19] 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 <zyn7109 at gmail.com>
Date: Sat, 17 Aug 2024 23:50:42 +0800
Subject: [PATCH 08/19] 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 <zyn7109 at gmail.com>
Date: Sun, 18 Aug 2024 00:30:32 +0800
Subject: [PATCH 09/19] 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 <zyn7109 at gmail.com>
Date: Sun, 18 Aug 2024 18:41:08 +0800
Subject: [PATCH 10/19] 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 <koraq at xs4all.nl>
Date: Sun, 18 Aug 2024 17:09:40 +0200
Subject: [PATCH 11/19] [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 <zyn7109 at gmail.com>
Date: Mon, 19 Aug 2024 09:53:37 +0800
Subject: [PATCH 12/19] 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 <zyn7109 at gmail.com>
Date: Wed, 28 Aug 2024 17:44:22 +0800
Subject: [PATCH 13/19] 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>
>From 974a0ad13f34fa8905ec22ee0b82e7a0a1e5af5d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 28 Aug 2024 18:00:47 +0800
Subject: [PATCH 14/19] Revert unrelated changes
---
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c93c0fcc1cc829..0e064be2391838 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -35,7 +35,6 @@
#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>
@@ -2177,7 +2176,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
return nullptr;
QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);
- if (false && TemplateParams && TemplateParams->size()) {
+ if (TemplateParams && TemplateParams->size()) {
auto *LastParam =
dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
if (LastParam && LastParam->isImplicit() &&
@@ -2589,7 +2588,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
return nullptr;
QualType T = adjustFunctionTypeForInstantiation(SemaRef.Context, D, TInfo);
- if (false && TemplateParams && TemplateParams->size()) {
+ if (TemplateParams && TemplateParams->size()) {
auto *LastParam =
dyn_cast<TemplateTypeParmDecl>(TemplateParams->asArray().back());
if (LastParam && LastParam->isImplicit() &&
>From d19e700121609d64e6081e4ee330d744e041072b Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 28 Aug 2024 18:05:06 +0800
Subject: [PATCH 15/19] oops, I forgot it
---
clang/www/cxx_dr_status.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 3a797710312707..88676593a57047 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -14029,7 +14029,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2369.html">2369</a></td>
<td>CD6</td>
<td>Ordering between constraints and substitution</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="partial" align="center">Partial</td>
</tr>
<tr id="2370">
<td><a href="https://cplusplus.github.io/CWG/issues/2370.html">2370</a></td>
>From 8ace714eb6563cf51e2181dfd9b0b2cb6e209679 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 8 Oct 2024 17:19:50 +0800
Subject: [PATCH 16/19] Simplify the implementation
---
clang/include/clang/Sema/Sema.h | 16 +--
clang/lib/Sema/SemaConcept.cpp | 105 +++++++---------
clang/lib/Sema/SemaTemplateDeduction.cpp | 14 ++-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 136 ++++++---------------
clang/lib/Sema/TreeTransform.h | 9 +-
5 files changed, 101 insertions(+), 179 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 16a26a17210d16..4dfee07e60d372 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13311,8 +13311,7 @@ class Sema final : public SemaBase {
// Must be used instead of SubstExpr at 'constraint checking' time.
ExprResult
SubstConstraintExpr(Expr *E,
- const MultiLevelTemplateArgumentList &TemplateArgs,
- DeclContext *InstantiatingFunctionDC = nullptr);
+ const MultiLevelTemplateArgumentList &TemplateArgs);
// Unlike the above, this does not evaluates constraints.
ExprResult SubstConstraintExprWithoutSatisfaction(
@@ -14428,15 +14427,10 @@ class Sema final : public SemaBase {
const MultiLevelTemplateArgumentList &TemplateArgs,
SourceRange TemplateIDRange);
- bool CheckInstantiatedFunctionTemplateConstraints(
- SourceLocation PointOfInstantiation, FunctionDecl *Decl,
- ArrayRef<TemplateArgument> TemplateArgs,
- ConstraintSatisfaction &Satisfaction);
-
- bool CheckFunctionConstraintsWithoutInstantiation(
- SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template,
- ArrayRef<TemplateArgument> TemplateArgs,
- ConstraintSatisfaction &Satisfaction);
+ bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation,
+ FunctionDecl *Decl,
+ ArrayRef<TemplateArgument> TemplateArgs,
+ ConstraintSatisfaction &Satisfaction);
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index a2d8e074c3bee7..7526cf7256e7b1 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -470,30 +470,10 @@ 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();
- 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,
- /*InstantiatingFunctionDC=*/FunctionDC);
+ S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) {
// C++2a [temp.constr.atomic]p1
@@ -1133,12 +1113,53 @@ bool Sema::EnsureTemplateArgumentListConstraints(
return false;
}
-bool Sema::CheckInstantiatedFunctionTemplateConstraints(
+static bool CheckFunctionConstraintsWithoutInstantiation(
+ Sema &SemaRef, SourceLocation PointOfInstantiation,
+ FunctionTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
+ ConstraintSatisfaction &Satisfaction) {
+ SmallVector<const Expr *, 3> TemplateAC;
+ Template->getAssociatedConstraints(TemplateAC);
+ if (TemplateAC.empty()) {
+ Satisfaction.IsSatisfied = true;
+ return false;
+ }
+
+ LocalInstantiationScope Scope(SemaRef);
+
+ FunctionDecl *FD = Template->getTemplatedDecl();
+ // 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 =
+ SemaRef.getTemplateInstantiationArgs(FD, NextDC,
+ /*Final=*/false,
+ /*Innermost=*/TemplateArgs,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true);
+
+ std::optional<Sema::CXXThisScopeRAII> ThisScope;
+ if (auto *Method = dyn_cast<CXXMethodDecl>(FD))
+ ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(),
+ /*ThisQuals=*/Method->getMethodQualifiers());
+ return SemaRef.CheckConstraintSatisfaction(
+ Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction);
+}
+
+bool Sema::CheckFunctionTemplateConstraints(
SourceLocation PointOfInstantiation, FunctionDecl *Decl,
ArrayRef<TemplateArgument> TemplateArgs,
ConstraintSatisfaction &Satisfaction) {
// In most cases we're not going to have constraints, so check for that first.
FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();
+
+ if (!Template)
+ return ::CheckFunctionConstraintsWithoutInstantiation(
+ *this, PointOfInstantiation, Decl->getDescribedFunctionTemplate(),
+ TemplateArgs, Satisfaction);
+
// Note - code synthesis context for the constraints check is created
// inside CheckConstraintsSatisfaction.
SmallVector<const Expr *, 3> TemplateAC;
@@ -1176,46 +1197,6 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints(
PointOfInstantiation, Satisfaction);
}
-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);
-
- 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 6d1ada9b41902d..bb54cda1f3f284 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4039,16 +4039,18 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
Owner = FD->getLexicalDeclContext();
}
- // C++20 [temp.deduct.general]p5: [DR2369]
+ // C++20 [temp.deduct.general]p5: (CWG2369)
// If the function template has associated constraints, those constraints are
// checked for satisfaction. If the constraints are not satisfied, type
// deduction fails.
- // FIXME: We haven't implemented DR2369 for lambdas yet, because we need
- // the captured variables to be instantiated in the scope.
+ // FIXME: We haven't implemented CWG2369 for lambdas yet, because we need
+ // to figure out how to instantiate lambda captures to the scope without
+ // first instantiating the lambda.
bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
if (!IsLambda && !IsIncomplete) {
- if (CheckFunctionConstraintsWithoutInstantiation(
- Info.getLocation(), FunctionTemplate->getCanonicalDecl(),
+ if (CheckFunctionTemplateConstraints(
+ Info.getLocation(),
+ FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
CanonicalBuilder, Info.AssociatedConstraintsSatisfaction))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
@@ -4104,7 +4106,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
// ([temp.constr.constr]). If the constraints are not satisfied, type
// deduction fails.
if (IsLambda && !IsIncomplete) {
- if (CheckInstantiatedFunctionTemplateConstraints(
+ if (CheckFunctionTemplateConstraints(
Info.getLocation(), Specialization, CanonicalBuilder,
Info.AssociatedConstraintsSatisfaction))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index bd67fa998ed4a5..10449637810e8f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1365,16 +1365,19 @@ namespace {
// Whether an incomplete substituion should be treated as an error.
bool BailOutOnIncomplete;
- // 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);
+ bool isSubstitutingConstraints() const {
+ return llvm::any_of(
+ llvm::reverse(SemaRef.CodeSynthesisContexts), [](auto &Context) {
+ return Context.Kind ==
+ Sema::CodeSynthesisContext::ConstraintSubstitution;
+ });
+ }
+
+ // CWG2770: Function parameters should be instantiated when they are
+ // needed by a satisfaction check of an atomic constraint or
+ // (recursively) by another function parameter.
+ bool maybeInstantiateFunctionParameterToScope(ParmVarDecl *OldParm);
public:
typedef TreeTransform<TemplateInstantiator> inherited;
@@ -1391,14 +1394,6 @@ namespace {
}
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.
///
@@ -1438,22 +1433,16 @@ namespace {
ArrayRef<UnexpandedParameterPack> Unexpanded,
bool &ShouldExpand, bool &RetainExpansion,
std::optional<unsigned> &NumExpansions) {
- if (ParameterInstantiationScope) {
+ if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) {
for (UnexpandedParameterPack ParmPack : Unexpanded) {
NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
- if (!VD)
+ if (!isa_and_present<ParmVarDecl>(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);
- }
+ if (maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(VD)))
+ return true;
}
}
+
return getSema().CheckParameterPacksForExpansion(
EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
RetainExpansion, NumExpansions);
@@ -1948,69 +1937,31 @@ 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);
+ if (SemaRef.CurrentInstantiationScope) {
+ if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) &&
+ maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D)))
+ return nullptr;
}
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;
- }
-
+bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope(
+ ParmVarDecl *OldParm) {
+ if (SemaRef.CurrentInstantiationScope->findInstantiationUnsafe(OldParm))
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,
- /*ExpectParameterPack=*/false);
- return !NewParm;
+ // The current context might have been changed in the process of transforming
+ // lambda expression. So resume it before we substitute into the parameter.
+ Sema::ContextRAII Context(SemaRef, OldParm->getDeclContext());
+
+ SmallVector<QualType> PTypes;
+ Sema::ExtParameterInfoBuilder TInfoBuilder;
+
+ return inherited::TransformFunctionTypeParams(
+ /*Loc=*/SourceLocation(), /*Params=*/OldParm, /*ParamTypes=*/nullptr,
+ /*ParamInfos=*/nullptr, /*PTypes=*/PTypes, /*PVars=*/nullptr,
+ TInfoBuilder, /*LastParamTransformed=*/nullptr,
+ /*IgnoreParameterIndex=*/true);
}
Decl *TemplateInstantiator::TransformDefinition(SourceLocation Loc, Decl *D) {
@@ -4551,17 +4502,10 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
ExprResult
Sema::SubstConstraintExpr(Expr *E,
- 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);
+ const MultiLevelTemplateArgumentList &TemplateArgs) {
+ // FIXME: should call SubstExpr directly if this function is equivalent or
+ // should it be different?
+ return SubstExpr(E, TemplateArgs);
}
ExprResult Sema::SubstConstraintExprWithoutSatisfaction(
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4497e49b57fe2f..8864771caf3bd2 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -722,7 +722,8 @@ class TreeTransform {
const QualType *ParamTypes,
const FunctionProtoType::ExtParameterInfo *ParamInfos,
SmallVectorImpl<QualType> &PTypes, SmallVectorImpl<ParmVarDecl *> *PVars,
- Sema::ExtParameterInfoBuilder &PInfos, unsigned *LastParamTransformed);
+ Sema::ExtParameterInfoBuilder &PInfos, unsigned *LastParamTransformed,
+ bool IgnoreParameterIndex = false);
bool TransformFunctionTypeParams(
SourceLocation Loc, ArrayRef<ParmVarDecl *> Params,
@@ -5997,8 +5998,8 @@ bool TreeTransform<Derived>::TransformFunctionTypeParams(
const FunctionProtoType::ExtParameterInfo *ParamInfos,
SmallVectorImpl<QualType> &OutParamTypes,
SmallVectorImpl<ParmVarDecl *> *PVars,
- Sema::ExtParameterInfoBuilder &PInfos,
- unsigned *LastParamTransformed) {
+ Sema::ExtParameterInfoBuilder &PInfos, unsigned *LastParamTransformed,
+ bool IgnoreParameterIndex) {
int indexAdjustment = 0;
unsigned NumParams = Params.size();
@@ -6006,7 +6007,7 @@ bool TreeTransform<Derived>::TransformFunctionTypeParams(
if (LastParamTransformed)
*LastParamTransformed = i;
if (ParmVarDecl *OldParm = Params[i]) {
- assert(OldParm->getFunctionScopeIndex() == i);
+ assert(IgnoreParameterIndex || OldParm->getFunctionScopeIndex() == i);
std::optional<unsigned> NumExpansions;
ParmVarDecl *NewParm = nullptr;
>From da61fab58911f7a5cf35d5bb1bbf795711a77fe8 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 8 Oct 2024 19:31:25 +0800
Subject: [PATCH 17/19] Add parentheses to sizeof expression
---
clang/test/CXX/drs/cwg27xx.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/CXX/drs/cwg27xx.cpp b/clang/test/CXX/drs/cwg27xx.cpp
index 1ccb6d96d2fa26..b0adf5690acc64 100644
--- a/clang/test/CXX/drs/cwg27xx.cpp
+++ b/clang/test/CXX/drs/cwg27xx.cpp
@@ -183,7 +183,7 @@ struct B {
};
template<typename T>
-int f(T t, typename B<T>::type u) requires (sizeof t == 1);
+int f(T t, typename B<T>::type u) requires (sizeof(t) == 1);
template<typename T>
int f(T t, long);
>From 1b8f0f62c79a5b2e536b5268cdc50d4389444e2c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 9 Oct 2024 10:09:55 +0800
Subject: [PATCH 18/19] Revert comment changes
---
clang/include/clang/Sema/Sema.h | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 48e99a56c96f7a..53c8a7e1839fe8 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13008,13 +13008,10 @@ class Sema final : public SemaBase {
///
/// \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 \p
- /// Innermost should likely be provided. If \p ND is non-null and \p
- /// Innermost is NULL, this is ignored.
+ /// Innermost should likely be provided. If \p ND is non-null, this is ignored.
///
/// \param Innermost if non-NULL, specifies a template argument list for the
- /// 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.
+ /// template declaration passed as \p ND.
///
/// \param RelativeToPrimary true if we should get the template
/// arguments relative to the primary template, even when we're
>From ef5acadd00205b906978c21cc8b1c902cc6dbec0 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 9 Oct 2024 12:37:59 +0800
Subject: [PATCH 19/19] Rebase on top of the new getTemplateInstantiationArgs()
---
clang/lib/Sema/SemaConcept.cpp | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 1df95a14e9ba02..c6987777137de5 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1126,14 +1126,11 @@ static bool CheckFunctionConstraintsWithoutInstantiation(
// 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 =
- SemaRef.getTemplateInstantiationArgs(FD, NextDC,
+ SemaRef.getTemplateInstantiationArgs(FD, /*DC=*/nullptr,
/*Final=*/false,
/*Innermost=*/TemplateArgs,
/*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
std::optional<Sema::CXXThisScopeRAII> ThisScope;
More information about the libcxx-commits
mailing list