[clang] [clang] CTAD: implement the missing IsDeducible constraint for alias templates (PR #89358)
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Mon May 13 07:33:11 PDT 2024
https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/89358
>From 20947c1a34fae3593a98dba125e5d70d652c7190 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Fri, 19 Apr 2024 10:54:12 +0200
Subject: [PATCH] [clang] CTAD: implement the missing IsDeducible constraint
for alias templates.
Fixes https://github.com/llvm/llvm-project/issues/85192
Fixes https://github.com/llvm/llvm-project/issues/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.
---
clang/include/clang/Basic/TokenKinds.def | 3 +
clang/include/clang/Sema/Sema.h | 9 ++
clang/lib/Basic/TypeTraits.cpp | 10 +++
clang/lib/Sema/SemaExprCXX.cpp | 10 ++-
clang/lib/Sema/SemaTemplate.cpp | 82 +++++++++++++++++--
clang/lib/Sema/SemaTemplateDeduction.cpp | 86 ++++++++++++++++++++
clang/test/AST/ast-dump-ctad-alias.cpp | 20 +++--
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 27 ++++--
clang/test/SemaTemplate/deduction-guide.cpp | 16 ++--
clang/www/cxx_status.html | 4 +-
10 files changed, 238 insertions(+), 29 deletions(-)
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 56c4b17f769d7..b5a0e9df9f7ae 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -538,6 +538,9 @@ TYPE_TRAIT_1(__can_pass_in_regs, CanPassInRegs, KEYCXX)
TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX)
TYPE_TRAIT_2(__reference_constructs_from_temporary, ReferenceConstructsFromTemporary, KEYCXX)
TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary, KEYCXX)
+// IsDeducible is only used internally by clang for CTAD implementation and
+// is not exposed to users.
+TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)
// Embarcadero Expression Traits
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4efd3878e861b..8cee7ab2c7b4e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9597,6 +9597,15 @@ class Sema final : public SemaBase {
ArrayRef<TemplateArgument> TemplateArgs,
sema::TemplateDeductionInfo &Info);
+ /// Deduce the template arguments of the given template from \p FromType.
+ /// Used to implement the IsDeducible constraint for alias CTAD per C++
+ /// [over.match.class.deduct]p4.
+ ///
+ /// It only supports class or type alias templates.
+ TemplateDeductionResult
+ DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType,
+ sema::TemplateDeductionInfo &Info);
+
TemplateDeductionResult DeduceTemplateArguments(
TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps,
ArrayRef<TemplateArgument> As, sema::TemplateDeductionInfo &Info,
diff --git a/clang/lib/Basic/TypeTraits.cpp b/clang/lib/Basic/TypeTraits.cpp
index 4dbf678dc395b..8d6794223ccaf 100644
--- a/clang/lib/Basic/TypeTraits.cpp
+++ b/clang/lib/Basic/TypeTraits.cpp
@@ -13,6 +13,7 @@
#include "clang/Basic/TypeTraits.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
+#include <cstring>
using namespace clang;
static constexpr const char *TypeTraitNames[] = {
@@ -81,6 +82,15 @@ const char *clang::getTraitName(UnaryExprOrTypeTrait T) {
const char *clang::getTraitSpelling(TypeTrait T) {
assert(T <= TT_Last && "invalid enum value!");
+ if (T == BTT_IsDeducible) {
+ // The __is_deducible is an internal-only type trait. To hide it from
+ // external users, we define it with an empty spelling name, preventing the
+ // clang parser from recognizing its token kind.
+ // However, other components such as the AST dump still require the real
+ // type trait name. Therefore, we return the real name when needed.
+ assert(std::strlen(TypeTraitSpellings[T]) == 0);
+ return "__is_deducible";
+ }
return TypeTraitSpellings[T];
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c181092113e1f..1cd912e3863d8 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6142,7 +6142,15 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs);
}
- default: llvm_unreachable("not a BTT");
+ case BTT_IsDeducible: {
+ const auto *TSTToBeDeduced = cast<DeducedTemplateSpecializationType>(LhsT);
+ sema::TemplateDeductionInfo Info(KeyLoc);
+ return Self.DeduceTemplateArgumentsFromType(
+ TSTToBeDeduced->getTemplateName().getAsTemplateDecl(), RhsT,
+ Info) == TemplateDeductionResult::Success;
+ }
+ default:
+ llvm_unreachable("not a BTT");
}
llvm_unreachable("Unknown type trait or not implemented");
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index bae00c6292703..e6402e99792a5 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2782,15 +2782,21 @@ NamedDecl *transformTemplateParameter(Sema &SemaRef, DeclContext *DC,
llvm_unreachable("Unhandled template parameter types");
}
-// Transform the require-clause of F if any.
+// Build the associated constraints for the alias deduction guides.
+// C++ [over.match.class.deduct]p3.3:
+// The associated constraints ([temp.constr.decl]) are the conjunction of the
+// associated constraints of g and a constraint that is satisfied if and only
+// if the arguments of A are deducible (see below) from the return type.
+//
// The return result is expected to be the require-clause for the synthesized
// alias deduction guide.
Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *F,
TypeAliasTemplateDecl *AliasTemplate,
- ArrayRef<DeducedTemplateArgument> DeduceResults) {
+ ArrayRef<DeducedTemplateArgument> DeduceResults,
+ Expr *IsDeducible) {
Expr *RC = F->getTemplateParameters()->getRequiresClause();
if (!RC)
- return nullptr;
+ return IsDeducible;
auto &Context = SemaRef.Context;
LocalInstantiationScope Scope(SemaRef);
@@ -2903,7 +2909,69 @@ Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *F,
ExprResult E = SemaRef.SubstExpr(RC, ArgsForBuildingRC);
if (E.isInvalid())
return nullptr;
- return E.getAs<Expr>();
+
+ auto Conjunction =
+ SemaRef.BuildBinOp(SemaRef.getCurScope(), SourceLocation{},
+ BinaryOperatorKind::BO_LAnd, E.get(), IsDeducible);
+ if (Conjunction.isInvalid())
+ return nullptr;
+ Conjunction.getAs<Expr>()->dump();
+ return Conjunction.getAs<Expr>();
+}
+// Build the is_deducible constraint for the alias deduction guides.
+// [over.match.class.deduct]p3.3:
+// ... and a constraint that is satisfied if and only if the arguments
+// of A are deducible (see below) from the return type.
+Expr *buildIsDeducibleConstraint(Sema &SemaRef,
+ TypeAliasTemplateDecl *AliasTemplate,
+ QualType ReturnType,
+ SmallVector<NamedDecl *> TemplateParams) {
+ auto &Context = SemaRef.Context;
+ // Constraint AST nodes must use uninstantiated depth.
+ if (auto *PrimaryTemplate =
+ AliasTemplate->getInstantiatedFromMemberTemplate()) {
+ LocalInstantiationScope Scope(SemaRef);
+
+ // Adjust the depth for TemplateParams.
+ unsigned AdjustDepth = PrimaryTemplate->getTemplateDepth();
+ SmallVector<TemplateArgument> TransformedTemplateArgs;
+ for (auto *TP : TemplateParams) {
+ // Rebuild any internal references to earlier parameters and reindex
+ // as we go.
+ MultiLevelTemplateArgumentList Args;
+ Args.setKind(TemplateSubstitutionKind::Rewrite);
+ Args.addOuterTemplateArguments(TransformedTemplateArgs);
+ NamedDecl *NewParam = transformTemplateParameter(
+ SemaRef, AliasTemplate->getDeclContext(), TP, Args,
+ /*NewIndex=*/TransformedTemplateArgs.size(),
+ getTemplateParameterDepth(TP) + AdjustDepth);
+
+ auto NewTemplateArgument = Context.getCanonicalTemplateArgument(
+ Context.getInjectedTemplateArg(NewParam));
+ TransformedTemplateArgs.push_back(NewTemplateArgument);
+ }
+ // Transformed the ReturnType to restore the uninstantiated depth.
+ MultiLevelTemplateArgumentList Args;
+ Args.setKind(TemplateSubstitutionKind::Rewrite);
+ Args.addOuterTemplateArguments(TransformedTemplateArgs);
+ ReturnType = SemaRef.SubstType(
+ ReturnType, Args, AliasTemplate->getLocation(),
+ Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate));
+ };
+
+ SmallVector<TypeSourceInfo *> IsDeducibleTypeTraitArgs = {
+ Context.getTrivialTypeSourceInfo(
+ Context.getDeducedTemplateSpecializationType(
+ TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
+ /*IsDependent=*/true)), // template specialization type whose
+ // arguments will be deduced.
+ Context.getTrivialTypeSourceInfo(
+ ReturnType), // type from which template arguments are deduced.
+ };
+ return TypeTraitExpr::Create(
+ Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),
+ TypeTrait::BTT_IsDeducible, IsDeducibleTypeTraitArgs,
+ AliasTemplate->getLocation(), /*Value*/ false);
}
std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>>
@@ -3112,8 +3180,10 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
auto *GG = cast<CXXDeductionGuideDecl>(FPrime);
- Expr *RequiresClause =
- transformRequireClause(SemaRef, F, AliasTemplate, DeduceResults);
+ Expr *IsDeducible = buildIsDeducibleConstraint(
+ SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams);
+ Expr *RequiresClause = transformRequireClause(SemaRef, F, AliasTemplate,
+ DeduceResults, IsDeducible);
// FIXME: implement the is_deducible constraint per C++
// [over.match.class.deduct]p3.3:
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index fe7e35d841510..e9f61fd87a018 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3237,6 +3237,40 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
return TemplateDeductionResult::Success;
}
+/// Complete template argument deduction for DeduceTemplateArgumentsFromType.
+/// FIXME: this is mostly duplicated with the above two versions. Deduplicate
+/// the three implementations.
+static TemplateDeductionResult FinishTemplateArgumentDeduction(
+ Sema &S, TemplateDecl *TD,
+ SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+ TemplateDeductionInfo &Info) {
+ // Unevaluated SFINAE context.
+ EnterExpressionEvaluationContext Unevaluated(
+ S, Sema::ExpressionEvaluationContext::Unevaluated);
+ Sema::SFINAETrap Trap(S);
+
+ Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(TD));
+
+ // C++ [temp.deduct.type]p2:
+ // [...] or if any template argument remains neither deduced nor
+ // explicitly specified, template argument deduction fails.
+ SmallVector<TemplateArgument, 4> SugaredBuilder, CanonicalBuilder;
+ if (auto Result = ConvertDeducedTemplateArguments(
+ S, TD, /*IsPartialOrdering=*/false, Deduced, Info, SugaredBuilder,
+ CanonicalBuilder);
+ Result != TemplateDeductionResult::Success)
+ return Result;
+
+ if (Trap.hasErrorOccurred())
+ return TemplateDeductionResult::SubstitutionFailure;
+
+ if (auto Result = CheckDeducedArgumentConstraints(S, TD, SugaredBuilder,
+ CanonicalBuilder, Info);
+ Result != TemplateDeductionResult::Success)
+ return Result;
+
+ return TemplateDeductionResult::Success;
+}
/// Perform template argument deduction to determine whether the given template
/// arguments match the given class or variable template partial specialization
@@ -3305,6 +3339,58 @@ Sema::DeduceTemplateArguments(VarTemplatePartialSpecializationDecl *Partial,
return ::DeduceTemplateArguments(*this, Partial, TemplateArgs, Info);
}
+TemplateDeductionResult
+Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType,
+ sema::TemplateDeductionInfo &Info) {
+ if (TD->isInvalidDecl())
+ return TemplateDeductionResult::Invalid;
+
+ QualType PType;
+ if (const auto *CTD = dyn_cast<ClassTemplateDecl>(TD)) {
+ // Use the InjectedClassNameType.
+ PType = Context.getTypeDeclType(CTD->getTemplatedDecl());
+ } else if (const auto *AliasTemplate = dyn_cast<TypeAliasTemplateDecl>(TD)) {
+ PType = AliasTemplate->getTemplatedDecl()
+ ->getUnderlyingType()
+ .getCanonicalType();
+ } else {
+ assert(false && "Expected a class or alias template");
+ }
+
+ // Unevaluated SFINAE context.
+ EnterExpressionEvaluationContext Unevaluated(
+ *this, Sema::ExpressionEvaluationContext::Unevaluated);
+ SFINAETrap Trap(*this);
+
+ // This deduction has no relation to any outer instantiation we might be
+ // performing.
+ LocalInstantiationScope InstantiationScope(*this);
+
+ SmallVector<DeducedTemplateArgument> Deduced(
+ TD->getTemplateParameters()->size());
+ SmallVector<TemplateArgument> PArgs = {TemplateArgument(PType)};
+ SmallVector<TemplateArgument> AArgs = {TemplateArgument(FromType)};
+ if (auto DeducedResult = DeduceTemplateArguments(
+ TD->getTemplateParameters(), PArgs, AArgs, Info, Deduced, false);
+ DeducedResult != TemplateDeductionResult::Success) {
+ return DeducedResult;
+ }
+
+ SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
+ InstantiatingTemplate Inst(*this, Info.getLocation(), TD, DeducedArgs, Info);
+ if (Inst.isInvalid())
+ return TemplateDeductionResult::InstantiationDepth;
+
+ if (Trap.hasErrorOccurred())
+ return TemplateDeductionResult::SubstitutionFailure;
+
+ TemplateDeductionResult Result;
+ runWithSufficientStackSpace(Info.getLocation(), [&] {
+ Result = ::FinishTemplateArgumentDeduction(*this, TD, Deduced, Info);
+ });
+ return Result;
+}
+
/// Determine whether the given type T is a simple-template-id type.
static bool isSimpleTemplateIdType(QualType T) {
if (const TemplateSpecializationType *Spec
diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp
index 423c3454ccb73..b975f8cc8e703 100644
--- a/clang/test/AST/ast-dump-ctad-alias.cpp
+++ b/clang/test/AST/ast-dump-ctad-alias.cpp
@@ -24,14 +24,24 @@ Out2<double>::AInner t(1.0);
// Verify that the require-clause of alias deduction guide is transformed correctly:
// - Occurrence T should be replaced with `int`;
// - Occurrence V should be replaced with the Y with depth 1
+// - Depth of cccurrence Y in the __is_deducible constraint should be 1
//
// CHECK: | `-FunctionTemplateDecl {{.*}} <deduction guide for AInner>
// CHECK-NEXT: | |-TemplateTypeParmDecl {{.*}} typename depth 0 index 0 Y
-// CHECK-NEXT: | |-UnresolvedLookupExpr {{.*}} '<dependent type>' lvalue (no ADL) = 'Concept'
-// CHECK-NEXT: | | |-TemplateArgument type 'int'
-// CHECK-NEXT: | | | `-BuiltinType {{.*}} 'int'
-// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-1-0'
-// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent depth 1 index 0
+// CHECK-NEXT: | |-BinaryOperator {{.*}} '<dependent type>' '&&'
+// CHECK-NEXT: | | |-UnresolvedLookupExpr {{.*}} '<dependent type>' lvalue (no ADL) = 'Concept'
+// CHECK-NEXT: | | | |-TemplateArgument type 'int'
+// CHECK-NEXT: | | | | `-BuiltinType {{.*}} 'int'
+// CHECK-NEXT: | | | `-TemplateArgument type 'type-parameter-1-0'
+// CHECK-NEXT: | | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent depth 1 index 0
+// CHECK-NEXT: | | `-TypeTraitExpr {{.*}} 'bool' __is_deducible
+// CHECK-NEXT: | | |-DeducedTemplateSpecializationType {{.*}} 'AInner' dependent
+// CHECK-NEXT: | | `-ElaboratedType {{.*}} 'Inner<type-parameter-1-0>' sugar dependent
+// CHECK-NEXT: | | `-TemplateSpecializationType {{.*}} 'Inner<type-parameter-1-0>' dependent Inner
+// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-1-0'
+// CHECK-NEXT: | | `-SubstTemplateTypeParmType {{.*}} 'type-parameter-1-0'
+// CHECK-NEXT: | | |-FunctionTemplate {{.*}} '<deduction guide for Inner>'
+// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent depth 1 index 0
// CHECK-NEXT: | |-CXXDeductionGuideDecl {{.*}} <deduction guide for AInner> 'auto (type-parameter-0-0) -> Inner<type-parameter-0-0>'
// CHECK-NEXT: | | `-ParmVarDecl {{.*}} 'type-parameter-0-0'
// CHECK-NEXT: | `-CXXDeductionGuideDecl {{.*}} used <deduction guide for AInner> 'auto (double) -> Inner<double>' implicit_instantiation
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 4c5595e409f28..7c186dc379c7b 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -109,10 +109,12 @@ struct Foo {
};
template <typename X, int Y>
-using Bar = Foo<X, sizeof(X)>;
+using Bar = Foo<X, sizeof(X)>; // expected-note {{candidate template ignored: couldn't infer template argument 'X'}} \
+ // expected-note {{candidate template ignored: constraints not satisfied [with X = int]}} \
+ // expected-note {{because '__is_deducible}}
-// FIXME: we should reject this case? GCC rejects it, MSVC accepts it.
-Bar s = {{1}};
+
+Bar s = {{1}}; // expected-error {{no viable constructor or deduction guide }}
} // namespace test9
namespace test10 {
@@ -133,9 +135,13 @@ A a(2); // Foo<int*>
namespace test11 {
struct A {};
template<class T> struct Foo { T c; };
-template<class X, class Y=A> using AFoo = Foo<Y>;
+template<class X, class Y=A>
+using AFoo = Foo<Y>; // expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0>' against 'int'}} \
+ // expected-note {{candidate template ignored: constraints not satisfied [with Y = int]}} \
+ // expected-note {{because '__is_deducible(AFoo, Foo<int>)' evaluated to false}} \
+ // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}}
-AFoo s = {1};
+AFoo s = {1}; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'AFoo'}}
} // namespace test11
namespace test12 {
@@ -190,13 +196,16 @@ template <class T> struct Foo { Foo(T); };
template<class V> using AFoo = Foo<V *>;
template<typename> concept False = false;
-template<False W> using BFoo = AFoo<W>;
+// FIXME: emit a more descriptive diagnostic for "__is_deducible" constraint failure.
+template<False W>
+using BFoo = AFoo<W>; // expected-note {{candidate template ignored: constraints not satisfied [with V = int]}} \
+ // expected-note {{because '__is_deducible(BFoo, Foo<int *>)' evaluated to false}} \
+ // expected-note {{candidate template ignored: could not match 'Foo<type-parameter-0-0 *>' against 'int *'}}
int i = 0;
AFoo a1(&i); // OK, deduce Foo<int *>
-// FIXME: we should reject this case as the W is not deduced from the deduced
-// type Foo<int *>.
-BFoo b2(&i);
+// the W is not deduced from the deduced type Foo<int *>.
+BFoo b2(&i); // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'BFoo'}}
} // namespace test15
namespace test16 {
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index 51e1eb49c5de7..a91ab5ec7bcc5 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -282,12 +282,16 @@ using AFoo = Foo<G<U>>;
// CHECK-LABEL: Dumping <deduction guide for AFoo>
// CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for AFoo>
// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} typename depth 0 index 0 U
-// CHECK-NEXT: |-ParenExpr {{.*}} 'bool'
-// CHECK-NEXT: | `-BinaryOperator {{.*}} 'bool' '=='
-// CHECK-NEXT: | |-UnaryExprOrTypeTraitExpr {{.*}} 'G<type-parameter-0-0>'
-// CHECK-NEXT: | `-ImplicitCastExpr {{.*}}
-// CHECK-NEXT: | `-IntegerLiteral {{.*}}
-// CHECK-NEXT: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for AFoo> 'auto (G<type-parameter-0-0>) -> Foo<G<type-parameter-0-0>>'
+// CHECK-NEXT: |-BinaryOperator {{.*}} '&&'
+// CHECK-NEXT: | |-ParenExpr {{.*}} 'bool'
+// CHECK-NEXT: | | `-BinaryOperator {{.*}} 'bool' '=='
+// CHECK-NEXT: | | |-UnaryExprOrTypeTraitExpr {{.*}} 'G<type-parameter-0-0>'
+// CHECK-NEXT: | | `-ImplicitCastExpr {{.*}}
+// CHECK-NEXT: | | `-IntegerLiteral {{.*}}
+// CHECK-NEXT: | `-TypeTraitExpr {{.*}} 'bool' __is_deducible
+// CHECK-NEXT: | |-DeducedTemplateSpecializationType {{.*}} 'AFoo' dependent
+// CHECK-NEXT: | `-TemplateSpecializationType {{.*}} 'Foo<G<type-parameter-0-0>>' dependent Foo
+// CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for AFoo> 'auto (G<type-parameter-0-0>) -> Foo<G<type-parameter-0-0>>'
// CHECK-NEXT: | `-ParmVarDecl {{.*}} 'G<type-parameter-0-0>'
// CHECK-NEXT: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for AFoo> 'auto (G<int>) -> Foo<G<int>>' implicit_instantiation
// CHECK-NEXT: |-TemplateArgument type 'int'
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 1338f544ffcb5..36cd97c44918f 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -870,8 +870,8 @@ <h2 id="cxx20">C++20 implementation status</h2>
<td class="partial" align="center">
<details>
<summary>Clang 19 (Partial)</summary>
- The associated constraints (over.match.class.deduct#3.3) for the
- synthesized deduction guides are not yet implemented.
+ This feature has been initially completed, but the feature macro
+ __cpp_deduction_guides has not been updated.
</details>
</td>
</tr>
More information about the cfe-commits
mailing list