Skip to content

Commit 128b3b6

Browse files
[Clang] Defer the instantiation of explicit-specifier until constraint checking completes (#70548)
Modifications: - Skip the instantiation of the explicit-specifier during Decl substitution if we are deducing template arguments and the explicit-specifier is value dependent. - Instantiate the explicit-specifier after the constraint checking completes. - Make `instantiateExplicitSpecifier` a member function in order to instantiate the explicit-specifier in different stages. This PR doesn’t defer the instantiation of the explicit specifier for deduction guides, because I’m not familiar with deduction guides yet. I’ll dig into it after this PR. According to my local test, GCC 13 tuple works with this PR. Fixes #59827. --------- Co-authored-by: Erich Keane <[email protected]>
1 parent 6576120 commit 128b3b6

File tree

5 files changed

+117
-14
lines changed

5 files changed

+117
-14
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,10 @@ Bug Fixes to C++ Support
682682
Fixes:
683683
(`#68769 <https://github.com/llvm/llvm-project/issues/68769>`_)
684684

685+
- Clang now defers the instantiation of explicit specifier until constraint checking
686+
completes (except deduction guides). Fixes:
687+
(`#59827 <https://github.com/llvm/llvm-project/issues/59827>`_)
688+
685689
Bug Fixes to AST Handling
686690
^^^^^^^^^^^^^^^^^^^^^^^^^
687691
- Fixed an import failure of recursive friend class template.

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10430,6 +10430,9 @@ class Sema final {
1043010430
const CXXConstructorDecl *Tmpl,
1043110431
const MultiLevelTemplateArgumentList &TemplateArgs);
1043210432

10433+
ExplicitSpecifier instantiateExplicitSpecifier(
10434+
const MultiLevelTemplateArgumentList &TemplateArgs, ExplicitSpecifier ES);
10435+
1043310436
NamedDecl *FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
1043410437
const MultiLevelTemplateArgumentList &TemplateArgs,
1043510438
bool FindingInstantiatedContext = false);

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3553,6 +3553,48 @@ static unsigned getPackIndexForParam(Sema &S,
35533553
llvm_unreachable("parameter index would not be produced from template");
35543554
}
35553555

3556+
// if `Specialization` is a `CXXConstructorDecl` or `CXXConversionDecl`,
3557+
// we'll try to instantiate and update its explicit specifier after constraint
3558+
// checking.
3559+
static Sema::TemplateDeductionResult instantiateExplicitSpecifierDeferred(
3560+
Sema &S, FunctionDecl *Specialization,
3561+
const MultiLevelTemplateArgumentList &SubstArgs,
3562+
TemplateDeductionInfo &Info, FunctionTemplateDecl *FunctionTemplate,
3563+
ArrayRef<TemplateArgument> DeducedArgs) {
3564+
auto GetExplicitSpecifier = [](FunctionDecl *D) {
3565+
return isa<CXXConstructorDecl>(D)
3566+
? cast<CXXConstructorDecl>(D)->getExplicitSpecifier()
3567+
: cast<CXXConversionDecl>(D)->getExplicitSpecifier();
3568+
};
3569+
auto SetExplicitSpecifier = [](FunctionDecl *D, ExplicitSpecifier ES) {
3570+
isa<CXXConstructorDecl>(D)
3571+
? cast<CXXConstructorDecl>(D)->setExplicitSpecifier(ES)
3572+
: cast<CXXConversionDecl>(D)->setExplicitSpecifier(ES);
3573+
};
3574+
3575+
ExplicitSpecifier ES = GetExplicitSpecifier(Specialization);
3576+
Expr *ExplicitExpr = ES.getExpr();
3577+
if (!ExplicitExpr)
3578+
return Sema::TDK_Success;
3579+
if (!ExplicitExpr->isValueDependent())
3580+
return Sema::TDK_Success;
3581+
3582+
Sema::InstantiatingTemplate Inst(
3583+
S, Info.getLocation(), FunctionTemplate, DeducedArgs,
3584+
Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution, Info);
3585+
if (Inst.isInvalid())
3586+
return Sema::TDK_InstantiationDepth;
3587+
Sema::SFINAETrap Trap(S);
3588+
const ExplicitSpecifier InstantiatedES =
3589+
S.instantiateExplicitSpecifier(SubstArgs, ES);
3590+
if (InstantiatedES.isInvalid() || Trap.hasErrorOccurred()) {
3591+
Specialization->setInvalidDecl(true);
3592+
return Sema::TDK_SubstitutionFailure;
3593+
}
3594+
SetExplicitSpecifier(Specialization, InstantiatedES);
3595+
return Sema::TDK_Success;
3596+
}
3597+
35563598
/// Finish template argument deduction for a function template,
35573599
/// checking the deduced template arguments for completeness and forming
35583600
/// the function template specialization.
@@ -3682,6 +3724,17 @@ Sema::TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
36823724
}
36833725
}
36843726

3727+
// We skipped the instantiation of the explicit-specifier during the
3728+
// substitution of `FD` before. So, we try to instantiate it back if
3729+
// `Specialization` is either a constructor or a conversion function.
3730+
if (isa<CXXConstructorDecl, CXXConversionDecl>(Specialization)) {
3731+
if (TDK_Success != instantiateExplicitSpecifierDeferred(
3732+
*this, Specialization, SubstArgs, Info,
3733+
FunctionTemplate, DeducedArgs)) {
3734+
return TDK_SubstitutionFailure;
3735+
}
3736+
}
3737+
36853738
if (OriginalCallArgs) {
36863739
// C++ [temp.deduct.call]p4:
36873740
// In general, the deduction process attempts to find template argument

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -563,26 +563,24 @@ static void instantiateDependentAMDGPUFlatWorkGroupSizeAttr(
563563
S.addAMDGPUFlatWorkGroupSizeAttr(New, Attr, MinExpr, MaxExpr);
564564
}
565565

566-
static ExplicitSpecifier
567-
instantiateExplicitSpecifier(Sema &S,
568-
const MultiLevelTemplateArgumentList &TemplateArgs,
569-
ExplicitSpecifier ES, FunctionDecl *New) {
566+
ExplicitSpecifier Sema::instantiateExplicitSpecifier(
567+
const MultiLevelTemplateArgumentList &TemplateArgs, ExplicitSpecifier ES) {
570568
if (!ES.getExpr())
571569
return ES;
572570
Expr *OldCond = ES.getExpr();
573571
Expr *Cond = nullptr;
574572
{
575573
EnterExpressionEvaluationContext Unevaluated(
576-
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
577-
ExprResult SubstResult = S.SubstExpr(OldCond, TemplateArgs);
574+
*this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
575+
ExprResult SubstResult = SubstExpr(OldCond, TemplateArgs);
578576
if (SubstResult.isInvalid()) {
579577
return ExplicitSpecifier::Invalid();
580578
}
581579
Cond = SubstResult.get();
582580
}
583581
ExplicitSpecifier Result(Cond, ES.getKind());
584582
if (!Cond->isTypeDependent())
585-
S.tryResolveExplicitSpecifier(Result);
583+
tryResolveExplicitSpecifier(Result);
586584
return Result;
587585
}
588586

@@ -2073,8 +2071,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
20732071

20742072
ExplicitSpecifier InstantiatedExplicitSpecifier;
20752073
if (auto *DGuide = dyn_cast<CXXDeductionGuideDecl>(D)) {
2076-
InstantiatedExplicitSpecifier = instantiateExplicitSpecifier(
2077-
SemaRef, TemplateArgs, DGuide->getExplicitSpecifier(), DGuide);
2074+
InstantiatedExplicitSpecifier = SemaRef.instantiateExplicitSpecifier(
2075+
TemplateArgs, DGuide->getExplicitSpecifier());
20782076
if (InstantiatedExplicitSpecifier.isInvalid())
20792077
return nullptr;
20802078
}
@@ -2453,11 +2451,25 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
24532451
}
24542452
}
24552453

2456-
ExplicitSpecifier InstantiatedExplicitSpecifier =
2457-
instantiateExplicitSpecifier(SemaRef, TemplateArgs,
2458-
ExplicitSpecifier::getFromDecl(D), D);
2459-
if (InstantiatedExplicitSpecifier.isInvalid())
2460-
return nullptr;
2454+
auto InstantiatedExplicitSpecifier = ExplicitSpecifier::getFromDecl(D);
2455+
// deduction guides need this
2456+
const bool CouldInstantiate =
2457+
InstantiatedExplicitSpecifier.getExpr() == nullptr ||
2458+
!InstantiatedExplicitSpecifier.getExpr()->isValueDependent();
2459+
2460+
// Delay the instantiation of the explicit-specifier until after the
2461+
// constraints are checked during template argument deduction.
2462+
if (CouldInstantiate ||
2463+
SemaRef.CodeSynthesisContexts.back().Kind !=
2464+
Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution) {
2465+
InstantiatedExplicitSpecifier = SemaRef.instantiateExplicitSpecifier(
2466+
TemplateArgs, InstantiatedExplicitSpecifier);
2467+
2468+
if (InstantiatedExplicitSpecifier.isInvalid())
2469+
return nullptr;
2470+
} else {
2471+
InstantiatedExplicitSpecifier.setKind(ExplicitSpecKind::Unresolved);
2472+
}
24612473

24622474
// Implicit destructors/constructors created for local classes in
24632475
// DeclareImplicit* (see SemaDeclCXX.cpp) might not have an associated TSI.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s
2+
3+
template <typename T1, typename T2> struct is_same {
4+
static constexpr bool value = false;
5+
};
6+
7+
template <typename T> struct is_same<T, T> {
8+
static constexpr bool value = true;
9+
};
10+
11+
template <class T, class U>
12+
concept SameHelper = is_same<T, U>::value;
13+
template <class T, class U>
14+
concept same_as = SameHelper<T, U> && SameHelper<U, T>;
15+
16+
namespace deferred_instantiation {
17+
template <class X> constexpr X do_not_instantiate() { return nullptr; }
18+
19+
struct T {
20+
template <same_as<float> X> explicit(do_not_instantiate<X>()) T(X) {}
21+
22+
T(int) {}
23+
};
24+
25+
T t(5);
26+
// expected-error@17{{cannot initialize}}
27+
// expected-note@20{{in instantiation of function template specialization}}
28+
// expected-note@30{{while substituting deduced template arguments}}
29+
// expected-note@30{{in instantiation of function template specialization}}
30+
T t2(5.0f);
31+
} // namespace deferred_instantiation

0 commit comments

Comments
 (0)