Skip to content

Commit 19a50f5

Browse files
committed
[clang] CTAD: implement the missing IsDeducible constraint for alias templates.
Fixes #85192 Fixes #84492
1 parent c515c78 commit 19a50f5

File tree

9 files changed

+243
-45
lines changed

9 files changed

+243
-45
lines changed

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ TYPE_TRAIT_1(__is_referenceable, IsReferenceable, KEYCXX)
537537
TYPE_TRAIT_1(__can_pass_in_regs, CanPassInRegs, KEYCXX)
538538
TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX)
539539
TYPE_TRAIT_2(__reference_constructs_from_temporary, ReferenceConstructsFromTemporary, KEYCXX)
540+
TYPE_TRAIT_2(__is_deducible, IsDeducible, KEYCXX)
540541

541542
// Embarcadero Expression Traits
542543
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9591,6 +9591,15 @@ class Sema final : public SemaBase {
95919591
ArrayRef<TemplateArgument> TemplateArgs,
95929592
sema::TemplateDeductionInfo &Info);
95939593

9594+
/// Deduce the template arguments of the given template from \p FromType.
9595+
/// Used to implement the IsDeducible constraint for alias CTAD per C++
9596+
/// [over.match.class.deduct]p4.
9597+
///
9598+
/// It only supports class or type alias templates.
9599+
TemplateDeductionResult
9600+
DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType,
9601+
sema::TemplateDeductionInfo &Info);
9602+
95949603
TemplateDeductionResult DeduceTemplateArguments(
95959604
TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps,
95969605
ArrayRef<TemplateArgument> As, sema::TemplateDeductionInfo &Info,

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3906,14 +3906,18 @@ ExprResult Parser::ParseTypeTrait() {
39063906
BalancedDelimiterTracker Parens(*this, tok::l_paren);
39073907
if (Parens.expectAndConsume())
39083908
return ExprError();
3909-
3909+
TypeTrait TTKind = TypeTraitFromTokKind(Kind);
39103910
SmallVector<ParsedType, 2> Args;
39113911
do {
39123912
// Parse the next type.
3913-
TypeResult Ty = ParseTypeName(/*SourceRange=*/nullptr,
3914-
getLangOpts().CPlusPlus
3915-
? DeclaratorContext::TemplateTypeArg
3916-
: DeclaratorContext::TypeName);
3913+
TypeResult Ty = ParseTypeName(
3914+
/*SourceRange=*/nullptr,
3915+
getLangOpts().CPlusPlus
3916+
// For __is_deducible type trait, the first argument is a template
3917+
// specification type without template argument lists.
3918+
? (TTKind == BTT_IsDeducible ? DeclaratorContext::TemplateArg
3919+
: DeclaratorContext::TemplateTypeArg)
3920+
: DeclaratorContext::TypeName);
39173921
if (Ty.isInvalid()) {
39183922
Parens.skipToEnd();
39193923
return ExprError();
@@ -3937,7 +3941,7 @@ ExprResult Parser::ParseTypeTrait() {
39373941

39383942
SourceLocation EndLoc = Parens.getCloseLocation();
39393943

3940-
return Actions.ActOnTypeTrait(TypeTraitFromTokKind(Kind), Loc, Args, EndLoc);
3944+
return Actions.ActOnTypeTrait(TTKind, Loc, Args, EndLoc);
39413945
}
39423946

39433947
/// ParseArrayTypeTrait - Parse the built-in array type-trait

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6100,6 +6100,17 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
61006100
tok::kw___is_pointer_interconvertible_base_of);
61016101

61026102
return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs);
6103+
}
6104+
case BTT_IsDeducible: {
6105+
if (const auto *TSTToBeDeduced =
6106+
LhsT->getAs<DeducedTemplateSpecializationType>()) {
6107+
sema::TemplateDeductionInfo Info(KeyLoc);
6108+
return Self.DeduceTemplateArgumentsFromType(
6109+
TSTToBeDeduced->getTemplateName().getAsTemplateDecl(), RhsT,
6110+
Info) == TemplateDeductionResult::Success;
6111+
}
6112+
// FIXME: emit a diagnostic.
6113+
return false;
61036114
}
61046115
default: llvm_unreachable("not a BTT");
61056116
}

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/ExprCXX.h"
1919
#include "clang/AST/RecursiveASTVisitor.h"
2020
#include "clang/AST/TemplateName.h"
21+
#include "clang/AST/Type.h"
2122
#include "clang/AST/TypeVisitor.h"
2223
#include "clang/Basic/Builtins.h"
2324
#include "clang/Basic/DiagnosticSema.h"
@@ -2780,6 +2781,41 @@ Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *FTD,
27802781
return E.getAs<Expr>();
27812782
}
27822783

2784+
// Build the associated constraints for the alias deduction guides.
2785+
// C++ [over.match.class.deduct]p3.3:
2786+
// The associated constraints ([temp.constr.decl]) are the conjunction of the
2787+
// associated constraints of g and a constraint that is satisfied if and only
2788+
// if the arguments of A are deducible (see below) from the return type.
2789+
Expr *
2790+
buildAssociatedConstraints(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate,
2791+
FunctionTemplateDecl *FTD,
2792+
llvm::ArrayRef<TemplateArgument> TransformedArgs,
2793+
QualType ReturnType) {
2794+
auto &Context = SemaRef.Context;
2795+
TemplateName TN(AliasTemplate);
2796+
auto TST = Context.getDeducedTemplateSpecializationType(TN, QualType(), true);
2797+
// Build the IsDeducible constraint.
2798+
SmallVector<TypeSourceInfo *> IsDeducibleTypeTraitArgs = {
2799+
Context.getTrivialTypeSourceInfo(TST),
2800+
Context.getTrivialTypeSourceInfo(ReturnType)};
2801+
Expr *IsDeducible = TypeTraitExpr::Create(
2802+
Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),
2803+
TypeTrait::BTT_IsDeducible, IsDeducibleTypeTraitArgs,
2804+
AliasTemplate->getLocation(), false);
2805+
2806+
// Substitute new template parameters into requires-clause if present.
2807+
if (auto *TransformedRC =
2808+
transformRequireClause(SemaRef, FTD, TransformedArgs)) {
2809+
auto Conjunction = SemaRef.BuildBinOp(
2810+
SemaRef.getCurScope(), SourceLocation{}, BinaryOperatorKind::BO_LAnd,
2811+
TransformedRC, IsDeducible);
2812+
if (Conjunction.isInvalid())
2813+
return nullptr;
2814+
return Conjunction.get();
2815+
}
2816+
return IsDeducible;
2817+
}
2818+
27832819
std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>>
27842820
getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) {
27852821
// Unwrap the sugared ElaboratedType.
@@ -2962,19 +2998,6 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
29622998
Context.getCanonicalTemplateArgument(
29632999
Context.getInjectedTemplateArg(NewParam));
29643000
}
2965-
// Substitute new template parameters into requires-clause if present.
2966-
Expr *RequiresClause =
2967-
transformRequireClause(SemaRef, F, TemplateArgsForBuildingFPrime);
2968-
// FIXME: implement the is_deducible constraint per C++
2969-
// [over.match.class.deduct]p3.3:
2970-
// ... and a constraint that is satisfied if and only if the arguments
2971-
// of A are deducible (see below) from the return type.
2972-
auto *FPrimeTemplateParamList = TemplateParameterList::Create(
2973-
Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
2974-
AliasTemplate->getTemplateParameters()->getLAngleLoc(),
2975-
FPrimeTemplateParams,
2976-
AliasTemplate->getTemplateParameters()->getRAngleLoc(),
2977-
/*RequiresClause=*/RequiresClause);
29783001

29793002
// To form a deduction guide f' from f, we leverage clang's instantiation
29803003
// mechanism, we construct a template argument list where the template
@@ -3019,6 +3042,18 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
30193042
if (auto *FPrime = SemaRef.InstantiateFunctionDeclaration(
30203043
F, TemplateArgListForBuildingFPrime, AliasTemplate->getLocation(),
30213044
Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
3045+
Expr *RequireClause = buildAssociatedConstraints(
3046+
SemaRef, AliasTemplate, F, TemplateArgsForBuildingFPrime,
3047+
FPrime->getReturnType());
3048+
if (!RequireClause)
3049+
continue;
3050+
auto *FPrimeTemplateParamList = TemplateParameterList::Create(
3051+
Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
3052+
AliasTemplate->getTemplateParameters()->getLAngleLoc(),
3053+
FPrimeTemplateParams,
3054+
AliasTemplate->getTemplateParameters()->getRAngleLoc(),
3055+
RequireClause);
3056+
30223057
auto *GG = cast<CXXDeductionGuideDecl>(FPrime);
30233058
buildDeductionGuide(SemaRef, AliasTemplate, FPrimeTemplateParamList,
30243059
GG->getCorrespondingConstructor(),
@@ -3072,23 +3107,25 @@ FunctionTemplateDecl *DeclareAggregateDeductionGuideForTypeAlias(
30723107
SemaRef.Context.getInjectedTemplateArg(NewParam));
30733108
TransformedTemplateParams.push_back(NewParam);
30743109
}
3075-
// FIXME: implement the is_deducible constraint per C++
3076-
// [over.match.class.deduct]p3.3.
3077-
Expr *TransformedRequiresClause = transformRequireClause(
3078-
SemaRef, RHSDeductionGuide, TransformedTemplateArgs);
3079-
auto *TransformedTemplateParameterList = TemplateParameterList::Create(
3080-
SemaRef.Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
3081-
AliasTemplate->getTemplateParameters()->getLAngleLoc(),
3082-
TransformedTemplateParams,
3083-
AliasTemplate->getTemplateParameters()->getRAngleLoc(),
3084-
TransformedRequiresClause);
30853110
auto *TransformedTemplateArgList = TemplateArgumentList::CreateCopy(
30863111
SemaRef.Context, TransformedTemplateArgs);
30873112

30883113
if (auto *TransformedDeductionGuide = SemaRef.InstantiateFunctionDeclaration(
30893114
RHSDeductionGuide, TransformedTemplateArgList,
30903115
AliasTemplate->getLocation(),
30913116
Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
3117+
Expr *RequireClause = buildAssociatedConstraints(
3118+
SemaRef, AliasTemplate, RHSDeductionGuide, TransformedTemplateArgs,
3119+
TransformedDeductionGuide->getReturnType());
3120+
if (!RequireClause)
3121+
return nullptr;
3122+
auto *TransformedTemplateParameterList = TemplateParameterList::Create(
3123+
SemaRef.Context,
3124+
AliasTemplate->getTemplateParameters()->getTemplateLoc(),
3125+
AliasTemplate->getTemplateParameters()->getLAngleLoc(),
3126+
TransformedTemplateParams,
3127+
AliasTemplate->getTemplateParameters()->getRAngleLoc(), RequireClause);
3128+
30923129
auto *GD =
30933130
llvm::dyn_cast<clang::CXXDeductionGuideDecl>(TransformedDeductionGuide);
30943131
FunctionTemplateDecl *Result = buildDeductionGuide(

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3139,6 +3139,40 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
31393139

31403140
return TemplateDeductionResult::Success;
31413141
}
3142+
/// Complete template argument deduction for DeduceTemplateArgumentsFromType.
3143+
/// FIXME: this is mostly duplicated with the above two versions. Deduplicate
3144+
/// the three implementations.
3145+
static TemplateDeductionResult FinishTemplateArgumentDeduction(
3146+
Sema &S, TemplateDecl *TD,
3147+
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
3148+
TemplateDeductionInfo &Info) {
3149+
// Unevaluated SFINAE context.
3150+
EnterExpressionEvaluationContext Unevaluated(
3151+
S, Sema::ExpressionEvaluationContext::Unevaluated);
3152+
Sema::SFINAETrap Trap(S);
3153+
3154+
Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(TD));
3155+
3156+
// C++ [temp.deduct.type]p2:
3157+
// [...] or if any template argument remains neither deduced nor
3158+
// explicitly specified, template argument deduction fails.
3159+
SmallVector<TemplateArgument, 4> SugaredBuilder, CanonicalBuilder;
3160+
if (auto Result = ConvertDeducedTemplateArguments(
3161+
S, TD, /*IsPartialOrdering=*/false, Deduced, Info, SugaredBuilder,
3162+
CanonicalBuilder);
3163+
Result != TemplateDeductionResult::Success)
3164+
return Result;
3165+
3166+
if (Trap.hasErrorOccurred())
3167+
return TemplateDeductionResult::SubstitutionFailure;
3168+
3169+
if (auto Result = CheckDeducedArgumentConstraints(S, TD, SugaredBuilder,
3170+
CanonicalBuilder, Info);
3171+
Result != TemplateDeductionResult::Success)
3172+
return Result;
3173+
3174+
return TemplateDeductionResult::Success;
3175+
}
31423176

31433177
/// Perform template argument deduction to determine whether the given template
31443178
/// arguments match the given class or variable template partial specialization
@@ -3207,6 +3241,59 @@ Sema::DeduceTemplateArguments(VarTemplatePartialSpecializationDecl *Partial,
32073241
return ::DeduceTemplateArguments(*this, Partial, TemplateArgs, Info);
32083242
}
32093243

3244+
TemplateDeductionResult
3245+
Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType,
3246+
sema::TemplateDeductionInfo &Info) {
3247+
if (TD->isInvalidDecl())
3248+
return TemplateDeductionResult::Invalid;
3249+
3250+
QualType PType;
3251+
if (const auto *CTD = dyn_cast<ClassTemplateDecl>(TD)) {
3252+
// Use the InjectedClassNameType.
3253+
PType = Context.getTypeDeclType(CTD->getTemplatedDecl());
3254+
} else if (const auto *AliasTemplate = dyn_cast<TypeAliasTemplateDecl>(TD)) {
3255+
PType = AliasTemplate->getTemplatedDecl()
3256+
->getUnderlyingType()
3257+
.getCanonicalType();
3258+
} else {
3259+
// FIXME: emit a diagnostic, we only only support alias and class templates.
3260+
return TemplateDeductionResult::Invalid;
3261+
}
3262+
3263+
// Unevaluated SFINAE context.
3264+
EnterExpressionEvaluationContext Unevaluated(
3265+
*this, Sema::ExpressionEvaluationContext::Unevaluated);
3266+
SFINAETrap Trap(*this);
3267+
3268+
// This deduction has no relation to any outer instantiation we might be
3269+
// performing.
3270+
LocalInstantiationScope InstantiationScope(*this);
3271+
3272+
SmallVector<DeducedTemplateArgument> Deduced(
3273+
TD->getTemplateParameters()->size());
3274+
SmallVector<TemplateArgument> PArgs = {TemplateArgument(PType)};
3275+
SmallVector<TemplateArgument> AArgs = {TemplateArgument(FromType)};
3276+
if (auto DeducedResult = DeduceTemplateArguments(
3277+
TD->getTemplateParameters(), PArgs, AArgs, Info, Deduced, false);
3278+
DeducedResult != TemplateDeductionResult::Success) {
3279+
return DeducedResult;
3280+
}
3281+
3282+
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
3283+
InstantiatingTemplate Inst(*this, Info.getLocation(), TD, DeducedArgs, Info);
3284+
if (Inst.isInvalid())
3285+
return TemplateDeductionResult::InstantiationDepth;
3286+
3287+
if (Trap.hasErrorOccurred())
3288+
return TemplateDeductionResult::SubstitutionFailure;
3289+
3290+
TemplateDeductionResult Result;
3291+
runWithSufficientStackSpace(Info.getLocation(), [&] {
3292+
Result = ::FinishTemplateArgumentDeduction(*this, TD, Deduced, Info);
3293+
});
3294+
return Result;
3295+
}
3296+
32103297
/// Determine whether the given type T is a simple-template-id type.
32113298
static bool isSimpleTemplateIdType(QualType T) {
32123299
if (const TemplateSpecializationType *Spec

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,12 @@ struct Foo {
109109
};
110110

111111
template <typename X, int Y>
112-
using Bar = Foo<X, sizeof(X)>;
112+
using Bar = Foo<X, sizeof(X)>; // expected-note {{candidate template ignored: couldn't infer template argument 'X'}} \
113+
// expected-note {{candidate template ignored: constraints not satisfied [with X = int]}} \
114+
// expected-note {{because '__is_deducible(Bar, Foo<int, 4UL>)' evaluated to false}}
113115

114-
// FIXME: we should reject this case? GCC rejects it, MSVC accepts it.
115-
Bar s = {{1}};
116+
117+
Bar s = {{1}}; // expected-error {{no viable constructor or deduction guide }}
116118
} // namespace test9
117119

118120
namespace test10 {
@@ -133,9 +135,13 @@ A a(2); // Foo<int*>
133135
namespace test11 {
134136
struct A {};
135137
template<class T> struct Foo { T c; };
136-
template<class X, class Y=A> using AFoo = Foo<Y>;
138+
template<class X, class Y=A>
139+
using AFoo = Foo<Y>; // expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0>' against 'int'}} \
140+
// expected-note {{candidate template ignored: constraints not satisfied [with T = int]}} \
141+
// expected-note {{because '__is_deducible(AFoo, Foo<int>)' evaluated to false}} \
142+
// expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}}
137143

138-
AFoo s = {1};
144+
AFoo s = {1}; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'AFoo'}}
139145
} // namespace test11
140146

141147
namespace test12 {
@@ -190,13 +196,15 @@ template <class T> struct Foo { Foo(T); };
190196

191197
template<class V> using AFoo = Foo<V *>;
192198
template<typename> concept False = false;
193-
template<False W> using BFoo = AFoo<W>;
199+
template<False W>
200+
using BFoo = AFoo<W>; // expected-note {{candidate template ignored: constraints not satisfied [with V = int]}} \
201+
// expected-note {{because '__is_deducible(BFoo, Foo<int *>)' evaluated to false}} \
202+
// expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0 *>' against 'int *'}}
194203
int i = 0;
195204
AFoo a1(&i); // OK, deduce Foo<int *>
196205

197-
// FIXME: we should reject this case as the W is not deduced from the deduced
198-
// type Foo<int *>.
199-
BFoo b2(&i);
206+
// the W is not deduced from the deduced type Foo<int *>.
207+
BFoo b2(&i); // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'BFoo'}}
200208
} // namespace test15
201209

202210
namespace test16 {

0 commit comments

Comments
 (0)