Skip to content

Commit 1205371

Browse files
authored
[clang] CTAD: Generate deduction guides for alias templates from non-template explicit deduction guides (#96686)
This patch addresses an issue where non-template explicit deduction guides were not considered when synthesized the deduction guides for alias templates. Fixes #94927.
1 parent a37c35e commit 1205371

File tree

2 files changed

+97
-8
lines changed

2 files changed

+97
-8
lines changed

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2307,8 +2307,12 @@ class ExtractTypeForDeductionGuide
23072307
}
23082308
};
23092309

2310-
// Build a deduction guide with the specified parameter types.
2311-
FunctionTemplateDecl *buildDeductionGuide(
2310+
// Build a deduction guide using the provided information.
2311+
//
2312+
// A deduction guide can be either a template or a non-template function
2313+
// declaration. If \p TemplateParams is null, a non-template function
2314+
// declaration will be created.
2315+
NamedDecl *buildDeductionGuide(
23122316
Sema &SemaRef, TemplateDecl *OriginalTemplate,
23132317
TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor,
23142318
ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart,
@@ -2334,16 +2338,21 @@ FunctionTemplateDecl *buildDeductionGuide(
23342338
Param->setDeclContext(Guide);
23352339
for (auto *TD : MaterializedTypedefs)
23362340
TD->setDeclContext(Guide);
2341+
if (isa<CXXRecordDecl>(DC))
2342+
Guide->setAccess(AS_public);
2343+
2344+
if (!TemplateParams) {
2345+
DC->addDecl(Guide);
2346+
return Guide;
2347+
}
23372348

23382349
auto *GuideTemplate = FunctionTemplateDecl::Create(
23392350
SemaRef.Context, DC, Loc, DeductionGuideName, TemplateParams, Guide);
23402351
GuideTemplate->setImplicit(IsImplicit);
23412352
Guide->setDescribedFunctionTemplate(GuideTemplate);
23422353

2343-
if (isa<CXXRecordDecl>(DC)) {
2344-
Guide->setAccess(AS_public);
2354+
if (isa<CXXRecordDecl>(DC))
23452355
GuideTemplate->setAccess(AS_public);
2346-
}
23472356

23482357
DC->addDecl(GuideTemplate);
23492358
return GuideTemplate;
@@ -2990,7 +2999,8 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
29902999
ASTContext &Context = SemaRef.Context;
29913000
// Constraint AST nodes must use uninstantiated depth.
29923001
if (auto *PrimaryTemplate =
2993-
AliasTemplate->getInstantiatedFromMemberTemplate()) {
3002+
AliasTemplate->getInstantiatedFromMemberTemplate();
3003+
PrimaryTemplate && TemplateParams.size() > 0) {
29943004
LocalInstantiationScope Scope(SemaRef);
29953005

29963006
// Adjust the depth for TemplateParams.
@@ -3254,12 +3264,12 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
32543264
FPrimeTemplateParams,
32553265
AliasTemplate->getTemplateParameters()->getRAngleLoc(),
32563266
/*RequiresClause=*/RequiresClause);
3257-
FunctionTemplateDecl *Result = buildDeductionGuide(
3267+
auto *Result = cast<FunctionTemplateDecl>(buildDeductionGuide(
32583268
SemaRef, AliasTemplate, FPrimeTemplateParamList,
32593269
GG->getCorrespondingConstructor(), GG->getExplicitSpecifier(),
32603270
GG->getTypeSourceInfo(), AliasTemplate->getBeginLoc(),
32613271
AliasTemplate->getLocation(), AliasTemplate->getEndLoc(),
3262-
F->isImplicit());
3272+
F->isImplicit()));
32633273
cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl())
32643274
->setDeductionCandidateKind(GG->getDeductionCandidateKind());
32653275
return Result;
@@ -3290,6 +3300,44 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
32903300
Guides.suppressDiagnostics();
32913301

32923302
for (auto *G : Guides) {
3303+
if (auto *DG = dyn_cast<CXXDeductionGuideDecl>(G)) {
3304+
// The deduction guide is a non-template function decl, we just clone it.
3305+
auto *FunctionType =
3306+
SemaRef.Context.getTrivialTypeSourceInfo(DG->getType());
3307+
FunctionProtoTypeLoc FPTL =
3308+
FunctionType->getTypeLoc().castAs<FunctionProtoTypeLoc>();
3309+
3310+
// Clone the parameters.
3311+
for (unsigned I = 0, N = DG->getNumParams(); I != N; ++I) {
3312+
const auto *P = DG->getParamDecl(I);
3313+
auto *TSI = SemaRef.Context.getTrivialTypeSourceInfo(P->getType());
3314+
ParmVarDecl *NewParam = ParmVarDecl::Create(
3315+
SemaRef.Context, G->getDeclContext(),
3316+
DG->getParamDecl(I)->getBeginLoc(), P->getLocation(), nullptr,
3317+
TSI->getType(), TSI, SC_None, nullptr);
3318+
NewParam->setScopeInfo(0, I);
3319+
FPTL.setParam(I, NewParam);
3320+
}
3321+
auto *Transformed = cast<FunctionDecl>(buildDeductionGuide(
3322+
SemaRef, AliasTemplate, /*TemplateParams=*/nullptr,
3323+
/*Constructor=*/nullptr, DG->getExplicitSpecifier(), FunctionType,
3324+
AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(),
3325+
AliasTemplate->getEndLoc(), DG->isImplicit()));
3326+
3327+
// FIXME: Here the synthesized deduction guide is not a templated
3328+
// function. Per [dcl.decl]p4, the requires-clause shall be present only
3329+
// if the declarator declares a templated function, a bug in standard?
3330+
auto *Constraint = buildIsDeducibleConstraint(
3331+
SemaRef, AliasTemplate, Transformed->getReturnType(), {});
3332+
if (auto *RC = DG->getTrailingRequiresClause()) {
3333+
auto Conjunction =
3334+
SemaRef.BuildBinOp(SemaRef.getCurScope(), SourceLocation{},
3335+
BinaryOperatorKind::BO_LAnd, RC, Constraint);
3336+
if (!Conjunction.isInvalid())
3337+
Constraint = Conjunction.getAs<Expr>();
3338+
}
3339+
Transformed->setTrailingRequiresClause(Constraint);
3340+
}
32933341
FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(G);
32943342
if (!F)
32953343
continue;

clang/test/SemaCXX/cxx20-ctad-type-alias.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,44 @@ template<typename...TS>
440440
using AA = A<int, TS...>;
441441
AA a{0};
442442
}
443+
444+
namespace GH94927 {
445+
template <typename T>
446+
struct A {
447+
A(T);
448+
};
449+
A(int) -> A<char>;
450+
451+
template <typename U>
452+
using B1 = A<U>;
453+
B1 b1(100); // deduce to A<char>;
454+
static_assert(__is_same(decltype(b1), A<char>));
455+
456+
template <typename U>
457+
requires (!__is_same(U, char)) // filter out the explicit deduction guide.
458+
using B2 = A<U>;
459+
template <typename V>
460+
using B3 = B2<V>;
461+
462+
B2 b2(100); // deduced to A<int>;
463+
static_assert(__is_same(decltype(b2), A<int>));
464+
B3 b3(100); // decuded to A<int>;
465+
static_assert(__is_same(decltype(b3), A<int>));
466+
467+
468+
// the nested case
469+
template <typename T1>
470+
struct Out {
471+
template <typename T2>
472+
struct A {
473+
A(T2);
474+
};
475+
A(int) -> A<T1>;
476+
477+
template <typename T3>
478+
using B = A<T3>;
479+
};
480+
481+
Out<float>::B out(100); // deduced to Out<float>::A<float>;
482+
static_assert(__is_same(decltype(out), Out<float>::A<float>));
483+
}

0 commit comments

Comments
 (0)