Skip to content

Commit a960573

Browse files
authored
[clang] CTAD: implement the missing IsDeducible constraint for alias templates (#89358)
Fixes #85192 Fixes #84492 This patch implements the "IsDeducible" constraint where the template arguments of the alias template can be deduced from the returned type of the synthesized deduction guide, per C++ [over.match.class.deduct]p4. In the implementation, we perform the deduction directly, which is more efficient than the way specified in the standard. Also update relevant CTAD tests which were incorrectly compiled due to the missing constraint.
1 parent 239f8b9 commit a960573

File tree

10 files changed

+241
-32
lines changed

10 files changed

+241
-32
lines changed

clang/include/clang/Basic/TokenKinds.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,9 @@ 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)
540540
TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary, KEYCXX)
541+
// IsDeducible is only used internally by clang for CTAD implementation and
542+
// is not exposed to users.
543+
TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)
541544

542545
// Embarcadero Expression Traits
543546
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
@@ -9492,6 +9492,15 @@ class Sema final : public SemaBase {
94929492
ArrayRef<TemplateArgument> TemplateArgs,
94939493
sema::TemplateDeductionInfo &Info);
94949494

9495+
/// Deduce the template arguments of the given template from \p FromType.
9496+
/// Used to implement the IsDeducible constraint for alias CTAD per C++
9497+
/// [over.match.class.deduct]p4.
9498+
///
9499+
/// It only supports class or type alias templates.
9500+
TemplateDeductionResult
9501+
DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType,
9502+
sema::TemplateDeductionInfo &Info);
9503+
94959504
TemplateDeductionResult DeduceTemplateArguments(
94969505
TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps,
94979506
ArrayRef<TemplateArgument> As, sema::TemplateDeductionInfo &Info,

clang/lib/Basic/TypeTraits.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "clang/Basic/TypeTraits.h"
1414
#include "llvm/Support/ErrorHandling.h"
1515
#include <cassert>
16+
#include <cstring>
1617
using namespace clang;
1718

1819
static constexpr const char *TypeTraitNames[] = {
@@ -81,6 +82,15 @@ const char *clang::getTraitName(UnaryExprOrTypeTrait T) {
8182

8283
const char *clang::getTraitSpelling(TypeTrait T) {
8384
assert(T <= TT_Last && "invalid enum value!");
85+
if (T == BTT_IsDeducible) {
86+
// The __is_deducible is an internal-only type trait. To hide it from
87+
// external users, we define it with an empty spelling name, preventing the
88+
// clang parser from recognizing its token kind.
89+
// However, other components such as the AST dump still require the real
90+
// type trait name. Therefore, we return the real name when needed.
91+
assert(std::strlen(TypeTraitSpellings[T]) == 0);
92+
return "__is_deducible";
93+
}
8494
return TypeTraitSpellings[T];
8595
}
8696

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6143,7 +6143,15 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
61436143

61446144
return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs);
61456145
}
6146-
default: llvm_unreachable("not a BTT");
6146+
case BTT_IsDeducible: {
6147+
const auto *TSTToBeDeduced = cast<DeducedTemplateSpecializationType>(LhsT);
6148+
sema::TemplateDeductionInfo Info(KeyLoc);
6149+
return Self.DeduceTemplateArgumentsFromType(
6150+
TSTToBeDeduced->getTemplateName().getAsTemplateDecl(), RhsT,
6151+
Info) == TemplateDeductionResult::Success;
6152+
}
6153+
default:
6154+
llvm_unreachable("not a BTT");
61476155
}
61486156
llvm_unreachable("Unknown type trait or not implemented");
61496157
}

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2782,17 +2782,24 @@ NamedDecl *transformTemplateParameter(Sema &SemaRef, DeclContext *DC,
27822782
llvm_unreachable("Unhandled template parameter types");
27832783
}
27842784

2785-
// Transform the require-clause of F if any.
2785+
// Build the associated constraints for the alias deduction guides.
2786+
// C++ [over.match.class.deduct]p3.3:
2787+
// The associated constraints ([temp.constr.decl]) are the conjunction of the
2788+
// associated constraints of g and a constraint that is satisfied if and only
2789+
// if the arguments of A are deducible (see below) from the return type.
2790+
//
27862791
// The return result is expected to be the require-clause for the synthesized
27872792
// alias deduction guide.
2788-
Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *F,
2789-
TypeAliasTemplateDecl *AliasTemplate,
2790-
ArrayRef<DeducedTemplateArgument> DeduceResults) {
2793+
Expr *
2794+
buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
2795+
TypeAliasTemplateDecl *AliasTemplate,
2796+
ArrayRef<DeducedTemplateArgument> DeduceResults,
2797+
Expr *IsDeducible) {
27912798
Expr *RC = F->getTemplateParameters()->getRequiresClause();
27922799
if (!RC)
2793-
return nullptr;
2800+
return IsDeducible;
27942801

2795-
auto &Context = SemaRef.Context;
2802+
ASTContext &Context = SemaRef.Context;
27962803
LocalInstantiationScope Scope(SemaRef);
27972804

27982805
// In the clang AST, constraint nodes are deliberately not instantiated unless
@@ -2903,7 +2910,68 @@ Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *F,
29032910
ExprResult E = SemaRef.SubstExpr(RC, ArgsForBuildingRC);
29042911
if (E.isInvalid())
29052912
return nullptr;
2906-
return E.getAs<Expr>();
2913+
2914+
auto Conjunction =
2915+
SemaRef.BuildBinOp(SemaRef.getCurScope(), SourceLocation{},
2916+
BinaryOperatorKind::BO_LAnd, E.get(), IsDeducible);
2917+
if (Conjunction.isInvalid())
2918+
return nullptr;
2919+
return Conjunction.getAs<Expr>();
2920+
}
2921+
// Build the is_deducible constraint for the alias deduction guides.
2922+
// [over.match.class.deduct]p3.3:
2923+
// ... and a constraint that is satisfied if and only if the arguments
2924+
// of A are deducible (see below) from the return type.
2925+
Expr *buildIsDeducibleConstraint(Sema &SemaRef,
2926+
TypeAliasTemplateDecl *AliasTemplate,
2927+
QualType ReturnType,
2928+
SmallVector<NamedDecl *> TemplateParams) {
2929+
ASTContext &Context = SemaRef.Context;
2930+
// Constraint AST nodes must use uninstantiated depth.
2931+
if (auto *PrimaryTemplate =
2932+
AliasTemplate->getInstantiatedFromMemberTemplate()) {
2933+
LocalInstantiationScope Scope(SemaRef);
2934+
2935+
// Adjust the depth for TemplateParams.
2936+
unsigned AdjustDepth = PrimaryTemplate->getTemplateDepth();
2937+
SmallVector<TemplateArgument> TransformedTemplateArgs;
2938+
for (auto *TP : TemplateParams) {
2939+
// Rebuild any internal references to earlier parameters and reindex
2940+
// as we go.
2941+
MultiLevelTemplateArgumentList Args;
2942+
Args.setKind(TemplateSubstitutionKind::Rewrite);
2943+
Args.addOuterTemplateArguments(TransformedTemplateArgs);
2944+
NamedDecl *NewParam = transformTemplateParameter(
2945+
SemaRef, AliasTemplate->getDeclContext(), TP, Args,
2946+
/*NewIndex=*/TransformedTemplateArgs.size(),
2947+
getTemplateParameterDepth(TP) + AdjustDepth);
2948+
2949+
auto NewTemplateArgument = Context.getCanonicalTemplateArgument(
2950+
Context.getInjectedTemplateArg(NewParam));
2951+
TransformedTemplateArgs.push_back(NewTemplateArgument);
2952+
}
2953+
// Transformed the ReturnType to restore the uninstantiated depth.
2954+
MultiLevelTemplateArgumentList Args;
2955+
Args.setKind(TemplateSubstitutionKind::Rewrite);
2956+
Args.addOuterTemplateArguments(TransformedTemplateArgs);
2957+
ReturnType = SemaRef.SubstType(
2958+
ReturnType, Args, AliasTemplate->getLocation(),
2959+
Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate));
2960+
};
2961+
2962+
SmallVector<TypeSourceInfo *> IsDeducibleTypeTraitArgs = {
2963+
Context.getTrivialTypeSourceInfo(
2964+
Context.getDeducedTemplateSpecializationType(
2965+
TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
2966+
/*IsDependent=*/true)), // template specialization type whose
2967+
// arguments will be deduced.
2968+
Context.getTrivialTypeSourceInfo(
2969+
ReturnType), // type from which template arguments are deduced.
2970+
};
2971+
return TypeTraitExpr::Create(
2972+
Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),
2973+
TypeTrait::BTT_IsDeducible, IsDeducibleTypeTraitArgs,
2974+
AliasTemplate->getLocation(), /*Value*/ false);
29072975
}
29082976

29092977
std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>>
@@ -3112,8 +3180,10 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
31123180
Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
31133181
auto *GG = cast<CXXDeductionGuideDecl>(FPrime);
31143182

3115-
Expr *RequiresClause =
3116-
transformRequireClause(SemaRef, F, AliasTemplate, DeduceResults);
3183+
Expr *IsDeducible = buildIsDeducibleConstraint(
3184+
SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams);
3185+
Expr *RequiresClause = buildAssociatedConstraints(
3186+
SemaRef, F, AliasTemplate, DeduceResults, IsDeducible);
31173187

31183188
// FIXME: implement the is_deducible constraint per C++
31193189
// [over.match.class.deduct]p3.3:

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3237,6 +3237,40 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
32373237

32383238
return TemplateDeductionResult::Success;
32393239
}
3240+
/// Complete template argument deduction for DeduceTemplateArgumentsFromType.
3241+
/// FIXME: this is mostly duplicated with the above two versions. Deduplicate
3242+
/// the three implementations.
3243+
static TemplateDeductionResult FinishTemplateArgumentDeduction(
3244+
Sema &S, TemplateDecl *TD,
3245+
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
3246+
TemplateDeductionInfo &Info) {
3247+
// Unevaluated SFINAE context.
3248+
EnterExpressionEvaluationContext Unevaluated(
3249+
S, Sema::ExpressionEvaluationContext::Unevaluated);
3250+
Sema::SFINAETrap Trap(S);
3251+
3252+
Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(TD));
3253+
3254+
// C++ [temp.deduct.type]p2:
3255+
// [...] or if any template argument remains neither deduced nor
3256+
// explicitly specified, template argument deduction fails.
3257+
SmallVector<TemplateArgument, 4> SugaredBuilder, CanonicalBuilder;
3258+
if (auto Result = ConvertDeducedTemplateArguments(
3259+
S, TD, /*IsPartialOrdering=*/false, Deduced, Info, SugaredBuilder,
3260+
CanonicalBuilder);
3261+
Result != TemplateDeductionResult::Success)
3262+
return Result;
3263+
3264+
if (Trap.hasErrorOccurred())
3265+
return TemplateDeductionResult::SubstitutionFailure;
3266+
3267+
if (auto Result = CheckDeducedArgumentConstraints(S, TD, SugaredBuilder,
3268+
CanonicalBuilder, Info);
3269+
Result != TemplateDeductionResult::Success)
3270+
return Result;
3271+
3272+
return TemplateDeductionResult::Success;
3273+
}
32403274

32413275
/// Perform template argument deduction to determine whether the given template
32423276
/// arguments match the given class or variable template partial specialization
@@ -3305,6 +3339,58 @@ Sema::DeduceTemplateArguments(VarTemplatePartialSpecializationDecl *Partial,
33053339
return ::DeduceTemplateArguments(*this, Partial, TemplateArgs, Info);
33063340
}
33073341

3342+
TemplateDeductionResult
3343+
Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType,
3344+
sema::TemplateDeductionInfo &Info) {
3345+
if (TD->isInvalidDecl())
3346+
return TemplateDeductionResult::Invalid;
3347+
3348+
QualType PType;
3349+
if (const auto *CTD = dyn_cast<ClassTemplateDecl>(TD)) {
3350+
// Use the InjectedClassNameType.
3351+
PType = Context.getTypeDeclType(CTD->getTemplatedDecl());
3352+
} else if (const auto *AliasTemplate = dyn_cast<TypeAliasTemplateDecl>(TD)) {
3353+
PType = AliasTemplate->getTemplatedDecl()
3354+
->getUnderlyingType()
3355+
.getCanonicalType();
3356+
} else {
3357+
assert(false && "Expected a class or alias template");
3358+
}
3359+
3360+
// Unevaluated SFINAE context.
3361+
EnterExpressionEvaluationContext Unevaluated(
3362+
*this, Sema::ExpressionEvaluationContext::Unevaluated);
3363+
SFINAETrap Trap(*this);
3364+
3365+
// This deduction has no relation to any outer instantiation we might be
3366+
// performing.
3367+
LocalInstantiationScope InstantiationScope(*this);
3368+
3369+
SmallVector<DeducedTemplateArgument> Deduced(
3370+
TD->getTemplateParameters()->size());
3371+
SmallVector<TemplateArgument> PArgs = {TemplateArgument(PType)};
3372+
SmallVector<TemplateArgument> AArgs = {TemplateArgument(FromType)};
3373+
if (auto DeducedResult = DeduceTemplateArguments(
3374+
TD->getTemplateParameters(), PArgs, AArgs, Info, Deduced, false);
3375+
DeducedResult != TemplateDeductionResult::Success) {
3376+
return DeducedResult;
3377+
}
3378+
3379+
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
3380+
InstantiatingTemplate Inst(*this, Info.getLocation(), TD, DeducedArgs, Info);
3381+
if (Inst.isInvalid())
3382+
return TemplateDeductionResult::InstantiationDepth;
3383+
3384+
if (Trap.hasErrorOccurred())
3385+
return TemplateDeductionResult::SubstitutionFailure;
3386+
3387+
TemplateDeductionResult Result;
3388+
runWithSufficientStackSpace(Info.getLocation(), [&] {
3389+
Result = ::FinishTemplateArgumentDeduction(*this, TD, Deduced, Info);
3390+
});
3391+
return Result;
3392+
}
3393+
33083394
/// Determine whether the given type T is a simple-template-id type.
33093395
static bool isSimpleTemplateIdType(QualType T) {
33103396
if (const TemplateSpecializationType *Spec

clang/test/AST/ast-dump-ctad-alias.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,24 @@ Out2<double>::AInner t(1.0);
2424
// Verify that the require-clause of alias deduction guide is transformed correctly:
2525
// - Occurrence T should be replaced with `int`;
2626
// - Occurrence V should be replaced with the Y with depth 1
27+
// - Depth of occurrence Y in the __is_deducible constraint should be 1
2728
//
2829
// CHECK: | `-FunctionTemplateDecl {{.*}} <deduction guide for AInner>
2930
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} typename depth 0 index 0 Y
30-
// CHECK-NEXT: | |-UnresolvedLookupExpr {{.*}} '<dependent type>' lvalue (no ADL) = 'Concept'
31-
// CHECK-NEXT: | | |-TemplateArgument type 'int'
32-
// CHECK-NEXT: | | | `-BuiltinType {{.*}} 'int'
33-
// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-1-0'
34-
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent depth 1 index 0
31+
// CHECK-NEXT: | |-BinaryOperator {{.*}} '<dependent type>' '&&'
32+
// CHECK-NEXT: | | |-UnresolvedLookupExpr {{.*}} '<dependent type>' lvalue (no ADL) = 'Concept'
33+
// CHECK-NEXT: | | | |-TemplateArgument type 'int'
34+
// CHECK-NEXT: | | | | `-BuiltinType {{.*}} 'int'
35+
// CHECK-NEXT: | | | `-TemplateArgument type 'type-parameter-1-0'
36+
// CHECK-NEXT: | | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent depth 1 index 0
37+
// CHECK-NEXT: | | `-TypeTraitExpr {{.*}} 'bool' __is_deducible
38+
// CHECK-NEXT: | | |-DeducedTemplateSpecializationType {{.*}} 'AInner' dependent
39+
// CHECK-NEXT: | | `-ElaboratedType {{.*}} 'Inner<type-parameter-1-0>' sugar dependent
40+
// CHECK-NEXT: | | `-TemplateSpecializationType {{.*}} 'Inner<type-parameter-1-0>' dependent Inner
41+
// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-1-0'
42+
// CHECK-NEXT: | | `-SubstTemplateTypeParmType {{.*}} 'type-parameter-1-0'
43+
// CHECK-NEXT: | | |-FunctionTemplate {{.*}} '<deduction guide for Inner>'
44+
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent depth 1 index 0
3545
// CHECK-NEXT: | |-CXXDeductionGuideDecl {{.*}} <deduction guide for AInner> 'auto (type-parameter-0-0) -> Inner<type-parameter-0-0>'
3646
// CHECK-NEXT: | | `-ParmVarDecl {{.*}} 'type-parameter-0-0'
3747
// CHECK-NEXT: | `-CXXDeductionGuideDecl {{.*}} used <deduction guide for AInner> 'auto (double) -> Inner<double>' implicit_instantiation

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

Lines changed: 18 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}}
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 Y = 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,16 @@ 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+
// FIXME: emit a more descriptive diagnostic for "__is_deducible" constraint failure.
200+
template<False W>
201+
using BFoo = AFoo<W>; // expected-note {{candidate template ignored: constraints not satisfied [with V = int]}} \
202+
// expected-note {{because '__is_deducible(BFoo, Foo<int *>)' evaluated to false}} \
203+
// expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0 *>' against 'int *'}}
194204
int i = 0;
195205
AFoo a1(&i); // OK, deduce Foo<int *>
196206

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

202211
namespace test16 {

clang/test/SemaTemplate/deduction-guide.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,16 @@ using AFoo = Foo<G<U>>;
282282
// CHECK-LABEL: Dumping <deduction guide for AFoo>
283283
// CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for AFoo>
284284
// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} typename depth 0 index 0 U
285-
// CHECK-NEXT: |-ParenExpr {{.*}} 'bool'
286-
// CHECK-NEXT: | `-BinaryOperator {{.*}} 'bool' '=='
287-
// CHECK-NEXT: | |-UnaryExprOrTypeTraitExpr {{.*}} 'G<type-parameter-0-0>'
288-
// CHECK-NEXT: | `-ImplicitCastExpr {{.*}}
289-
// CHECK-NEXT: | `-IntegerLiteral {{.*}}
290-
// CHECK-NEXT: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for AFoo> 'auto (G<type-parameter-0-0>) -> Foo<G<type-parameter-0-0>>'
285+
// CHECK-NEXT: |-BinaryOperator {{.*}} '&&'
286+
// CHECK-NEXT: | |-ParenExpr {{.*}} 'bool'
287+
// CHECK-NEXT: | | `-BinaryOperator {{.*}} 'bool' '=='
288+
// CHECK-NEXT: | | |-UnaryExprOrTypeTraitExpr {{.*}} 'G<type-parameter-0-0>'
289+
// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}}
290+
// CHECK-NEXT: | | `-IntegerLiteral {{.*}}
291+
// CHECK-NEXT: | `-TypeTraitExpr {{.*}} 'bool' __is_deducible
292+
// CHECK-NEXT: | |-DeducedTemplateSpecializationType {{.*}} 'AFoo' dependent
293+
// CHECK-NEXT: | `-TemplateSpecializationType {{.*}} 'Foo<G<type-parameter-0-0>>' dependent Foo
294+
// CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for AFoo> 'auto (G<type-parameter-0-0>) -> Foo<G<type-parameter-0-0>>'
291295
// CHECK-NEXT: | `-ParmVarDecl {{.*}} 'G<type-parameter-0-0>'
292296
// CHECK-NEXT: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for AFoo> 'auto (G<int>) -> Foo<G<int>>' implicit_instantiation
293297
// CHECK-NEXT: |-TemplateArgument type 'int'

clang/www/cxx_status.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -870,8 +870,8 @@ <h2 id="cxx20">C++20 implementation status</h2>
870870
<td class="partial" align="center">
871871
<details>
872872
<summary>Clang 19 (Partial)</summary>
873-
The associated constraints (over.match.class.deduct#3.3) for the
874-
synthesized deduction guides are not yet implemented.
873+
This feature has been initially completed, but the feature macro
874+
__cpp_deduction_guides has not been updated.
875875
</details>
876876
</td>
877877
</tr>

0 commit comments

Comments
 (0)