Skip to content

Commit 082c8e4

Browse files
LYP951018erichkeane
authored andcommitted
[Clang] Defer the instantiation of explicit-specifier until constraint checking completes (llvm#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 llvm#59827. --------- Co-authored-by: Erich Keane <[email protected]>
1 parent 98bfdac commit 082c8e4

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
@@ -853,6 +853,10 @@ Bug Fixes to C++ Support
853853
(`#64172 <https://github.com/llvm/llvm-project/issues/64172>`_) and
854854
(`#64723 <https://github.com/llvm/llvm-project/issues/64723>`_).
855855

856+
- Clang now defers the instantiation of explicit specifier until constraint checking
857+
completes (except deduction guides). Fixes:
858+
(`#59827 <https://github.com/llvm/llvm-project/issues/59827>`_)
859+
856860
Bug Fixes to AST Handling
857861
^^^^^^^^^^^^^^^^^^^^^^^^^
858862

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10293,6 +10293,9 @@ class Sema final {
1029310293
const CXXConstructorDecl *Tmpl,
1029410294
const MultiLevelTemplateArgumentList &TemplateArgs);
1029510295

10296+
ExplicitSpecifier instantiateExplicitSpecifier(
10297+
const MultiLevelTemplateArgumentList &TemplateArgs, ExplicitSpecifier ES);
10298+
1029610299
NamedDecl *FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
1029710300
const MultiLevelTemplateArgumentList &TemplateArgs,
1029810301
bool FindingInstantiatedContext = false);

clang/lib/Sema/SemaTemplateDeduction.cpp

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

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

3720+
// We skipped the instantiation of the explicit-specifier during the
3721+
// substitution of `FD` before. So, we try to instantiate it back if
3722+
// `Specialization` is either a constructor or a conversion function.
3723+
if (isa<CXXConstructorDecl, CXXConversionDecl>(Specialization)) {
3724+
if (TDK_Success != instantiateExplicitSpecifierDeferred(
3725+
*this, Specialization, SubstArgs, Info,
3726+
FunctionTemplate, DeducedArgs)) {
3727+
return TDK_SubstitutionFailure;
3728+
}
3729+
}
3730+
36783731
if (OriginalCallArgs) {
36793732
// C++ [temp.deduct.call]p4:
36803733
// 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
@@ -555,26 +555,24 @@ static void instantiateDependentAMDGPUFlatWorkGroupSizeAttr(
555555
S.addAMDGPUFlatWorkGroupSizeAttr(New, Attr, MinExpr, MaxExpr);
556556
}
557557

558-
static ExplicitSpecifier
559-
instantiateExplicitSpecifier(Sema &S,
560-
const MultiLevelTemplateArgumentList &TemplateArgs,
561-
ExplicitSpecifier ES, FunctionDecl *New) {
558+
ExplicitSpecifier Sema::instantiateExplicitSpecifier(
559+
const MultiLevelTemplateArgumentList &TemplateArgs, ExplicitSpecifier ES) {
562560
if (!ES.getExpr())
563561
return ES;
564562
Expr *OldCond = ES.getExpr();
565563
Expr *Cond = nullptr;
566564
{
567565
EnterExpressionEvaluationContext Unevaluated(
568-
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
569-
ExprResult SubstResult = S.SubstExpr(OldCond, TemplateArgs);
566+
*this, Sema::ExpressionEvaluationContext::ConstantEvaluated);
567+
ExprResult SubstResult = SubstExpr(OldCond, TemplateArgs);
570568
if (SubstResult.isInvalid()) {
571569
return ExplicitSpecifier::Invalid();
572570
}
573571
Cond = SubstResult.get();
574572
}
575573
ExplicitSpecifier Result(Cond, ES.getKind());
576574
if (!Cond->isTypeDependent())
577-
S.tryResolveExplicitSpecifier(Result);
575+
tryResolveExplicitSpecifier(Result);
578576
return Result;
579577
}
580578

@@ -2065,8 +2063,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
20652063

20662064
ExplicitSpecifier InstantiatedExplicitSpecifier;
20672065
if (auto *DGuide = dyn_cast<CXXDeductionGuideDecl>(D)) {
2068-
InstantiatedExplicitSpecifier = instantiateExplicitSpecifier(
2069-
SemaRef, TemplateArgs, DGuide->getExplicitSpecifier(), DGuide);
2066+
InstantiatedExplicitSpecifier = SemaRef.instantiateExplicitSpecifier(
2067+
TemplateArgs, DGuide->getExplicitSpecifier());
20702068
if (InstantiatedExplicitSpecifier.isInvalid())
20712069
return nullptr;
20722070
}
@@ -2440,11 +2438,25 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
24402438
}
24412439
}
24422440

2443-
ExplicitSpecifier InstantiatedExplicitSpecifier =
2444-
instantiateExplicitSpecifier(SemaRef, TemplateArgs,
2445-
ExplicitSpecifier::getFromDecl(D), D);
2446-
if (InstantiatedExplicitSpecifier.isInvalid())
2447-
return nullptr;
2441+
auto InstantiatedExplicitSpecifier = ExplicitSpecifier::getFromDecl(D);
2442+
// deduction guides need this
2443+
const bool CouldInstantiate =
2444+
InstantiatedExplicitSpecifier.getExpr() == nullptr ||
2445+
!InstantiatedExplicitSpecifier.getExpr()->isValueDependent();
2446+
2447+
// Delay the instantiation of the explicit-specifier until after the
2448+
// constraints are checked during template argument deduction.
2449+
if (CouldInstantiate ||
2450+
SemaRef.CodeSynthesisContexts.back().Kind !=
2451+
Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution) {
2452+
InstantiatedExplicitSpecifier = SemaRef.instantiateExplicitSpecifier(
2453+
TemplateArgs, InstantiatedExplicitSpecifier);
2454+
2455+
if (InstantiatedExplicitSpecifier.isInvalid())
2456+
return nullptr;
2457+
} else {
2458+
InstantiatedExplicitSpecifier.setKind(ExplicitSpecKind::Unresolved);
2459+
}
24482460

24492461
// Implicit destructors/constructors created for local classes in
24502462
// 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)