Skip to content

Commit 20947c1

Browse files
committed
[clang] CTAD: implement the missing IsDeducible constraint for alias templates.
Fixes #85192 Fixes #84492 - rebase to main - add release note for __is_deducible - implement diagnostics for bad argument types for __is_deducible Don't expose __is_deducible trait. Refine the implementation of hiding __is_deducible type trait. Apply approach 3.
1 parent d182877 commit 20947c1

File tree

10 files changed

+238
-29
lines changed

10 files changed

+238
-29
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
@@ -9597,6 +9597,15 @@ class Sema final : public SemaBase {
95979597
ArrayRef<TemplateArgument> TemplateArgs,
95989598
sema::TemplateDeductionInfo &Info);
95999599

9600+
/// Deduce the template arguments of the given template from \p FromType.
9601+
/// Used to implement the IsDeducible constraint for alias CTAD per C++
9602+
/// [over.match.class.deduct]p4.
9603+
///
9604+
/// It only supports class or type alias templates.
9605+
TemplateDeductionResult
9606+
DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType,
9607+
sema::TemplateDeductionInfo &Info);
9608+
96009609
TemplateDeductionResult DeduceTemplateArguments(
96019610
TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps,
96029611
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
@@ -6142,7 +6142,15 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
61426142

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

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2782,15 +2782,21 @@ 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.
27882793
Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *F,
27892794
TypeAliasTemplateDecl *AliasTemplate,
2790-
ArrayRef<DeducedTemplateArgument> DeduceResults) {
2795+
ArrayRef<DeducedTemplateArgument> DeduceResults,
2796+
Expr *IsDeducible) {
27912797
Expr *RC = F->getTemplateParameters()->getRequiresClause();
27922798
if (!RC)
2793-
return nullptr;
2799+
return IsDeducible;
27942800

27952801
auto &Context = SemaRef.Context;
27962802
LocalInstantiationScope Scope(SemaRef);
@@ -2903,7 +2909,69 @@ Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *F,
29032909
ExprResult E = SemaRef.SubstExpr(RC, ArgsForBuildingRC);
29042910
if (E.isInvalid())
29052911
return nullptr;
2906-
return E.getAs<Expr>();
2912+
2913+
auto Conjunction =
2914+
SemaRef.BuildBinOp(SemaRef.getCurScope(), SourceLocation{},
2915+
BinaryOperatorKind::BO_LAnd, E.get(), IsDeducible);
2916+
if (Conjunction.isInvalid())
2917+
return nullptr;
2918+
Conjunction.getAs<Expr>()->dump();
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+
auto &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 = transformRequireClause(SemaRef, F, AliasTemplate,
3186+
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 cccurrence 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)