Skip to content

Commit 11ceaed

Browse files
committed
Defer the instantiation of explicit-specifier after constraint checking
1 parent 1abd8d1 commit 11ceaed

File tree

5 files changed

+123
-14
lines changed

5 files changed

+123
-14
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,10 @@ Bug Fixes to C++ Support
670670
default initializing a base class in a constant expression context. Fixes:
671671
(`#69890 <https://github.com/llvm/llvm-project/issues/69890>`_)
672672

673+
- Clang now defers the instantiation of explicit specifier until constraint checking
674+
completes (except deduction guides). Fixes:
675+
(`#59827 <https://github.com/llvm/llvm-project/issues/59827>`_)
676+
673677
Bug Fixes to AST Handling
674678
^^^^^^^^^^^^^^^^^^^^^^^^^
675679
- 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: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3553,6 +3553,56 @@ 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 try to instantiate and update its explicit specifier after constraint
3558+
// checking.
3559+
static Sema::TemplateDeductionResult
3560+
tryInstantiateExplicitSpecifier(Sema &S, FunctionDecl *Specialization,
3561+
const MultiLevelTemplateArgumentList &SubstArgs,
3562+
TemplateDeductionInfo &Info,
3563+
FunctionTemplateDecl *FunctionTemplate,
3564+
ArrayRef<TemplateArgument> DeducedArgs) {
3565+
3566+
const auto TryInstantiateExplicitSpecifierForSingleDecl =
3567+
[&](auto *ExplicitDecl) {
3568+
ExplicitSpecifier ExplicitSpecifier =
3569+
ExplicitDecl->getExplicitSpecifier();
3570+
Expr *const Expr = ExplicitSpecifier.getExpr();
3571+
if (!Expr) {
3572+
return Sema::TDK_Success;
3573+
}
3574+
if (!Expr->isValueDependent()) {
3575+
return Sema::TDK_Success;
3576+
}
3577+
// TemplateDeclInstantiator::InitFunctionInstantiation set the
3578+
// ActiveInstType to TemplateInstantiation, but we need
3579+
// to enable SFINAE when instantiating explicit specifier.
3580+
Sema::InstantiatingTemplate Inst(
3581+
S, Info.getLocation(), FunctionTemplate, DeducedArgs,
3582+
Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution,
3583+
Info);
3584+
const auto Instantiated =
3585+
S.instantiateExplicitSpecifier(SubstArgs, ExplicitSpecifier);
3586+
if (Instantiated.isInvalid()) {
3587+
ExplicitDecl->setInvalidDecl(true);
3588+
return clang::Sema::TDK_SubstitutionFailure;
3589+
}
3590+
ExplicitDecl->setExplicitSpecifier(Instantiated);
3591+
return clang::Sema::TDK_Success;
3592+
};
3593+
Sema::TemplateDeductionResult DeductionResult = clang::Sema::TDK_Success;
3594+
if (CXXConstructorDecl *ConstructorDecl =
3595+
dyn_cast_or_null<CXXConstructorDecl>(Specialization)) {
3596+
DeductionResult =
3597+
TryInstantiateExplicitSpecifierForSingleDecl(ConstructorDecl);
3598+
} else if (CXXConversionDecl *ConversionDecl =
3599+
dyn_cast_or_null<CXXConversionDecl>(Specialization)) {
3600+
DeductionResult =
3601+
TryInstantiateExplicitSpecifierForSingleDecl(ConversionDecl);
3602+
}
3603+
return DeductionResult;
3604+
}
3605+
35563606
/// Finish template argument deduction for a function template,
35573607
/// checking the deduced template arguments for completeness and forming
35583608
/// the function template specialization.
@@ -3682,6 +3732,15 @@ Sema::TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
36823732
}
36833733
}
36843734

3735+
// We skipped the instantiation of the explicit-specifier during subst the
3736+
// decl before. Now, we try to instantiate it back if the Specialization is a
3737+
// constructor or a conversion.
3738+
if (TDK_Success !=
3739+
tryInstantiateExplicitSpecifier(*this, Specialization, SubstArgs, Info,
3740+
FunctionTemplate, DeducedArgs)) {
3741+
return TDK_SubstitutionFailure;
3742+
}
3743+
36853744
if (OriginalCallArgs) {
36863745
// C++ [temp.deduct.call]p4:
36873746
// 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)