[clang] a960573 - [clang] CTAD: implement the missing IsDeducible constraint for alias templates (#89358)
via cfe-commits
cfe-commits at lists.llvm.org
Thu May 16 04:01:22 PDT 2024
Author: Haojian Wu
Date: 2024-05-16T13:01:18+02:00
New Revision: a9605730a481707623358d3b12220f05cfdc50a8
URL: https://github.com/llvm/llvm-project/commit/a9605730a481707623358d3b12220f05cfdc50a8
DIFF: https://github.com/llvm/llvm-project/commit/a9605730a481707623358d3b12220f05cfdc50a8.diff
LOG: [clang] CTAD: implement the missing IsDeducible constraint for alias templates (#89358)
Fixes https://github.com/llvm/llvm-project/issues/85192
Fixes https://github.com/llvm/llvm-project/issues/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.
Added:
Modified:
clang/include/clang/Basic/TokenKinds.def
clang/include/clang/Sema/Sema.h
clang/lib/Basic/TypeTraits.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/test/AST/ast-dump-ctad-alias.cpp
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
clang/test/SemaTemplate/deduction-guide.cpp
clang/www/cxx_status.html
Removed:
################################################################################
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 6a414aa57f32b..66d5e2d4a4ade 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9492,6 +9492,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 34e12078a8c92..e4601f7d6c47d 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6143,7 +6143,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 c7aac068e264b..a5350ceb59cb7 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2782,17 +2782,24 @@ 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) {
+Expr *
+buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
+ TypeAliasTemplateDecl *AliasTemplate,
+ ArrayRef<DeducedTemplateArgument> DeduceResults,
+ Expr *IsDeducible) {
Expr *RC = F->getTemplateParameters()->getRequiresClause();
if (!RC)
- return nullptr;
+ return IsDeducible;
- auto &Context = SemaRef.Context;
+ ASTContext &Context = SemaRef.Context;
LocalInstantiationScope Scope(SemaRef);
// In the clang AST, constraint nodes are deliberately not instantiated unless
@@ -2903,7 +2910,68 @@ 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;
+ 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) {
+ ASTContext &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 = buildAssociatedConstraints(
+ 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 b5d405111fe4c..4d89fcfc9b116 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..7fe6c05621eee 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 occurrence 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 06777eaa6df69..6e2fd745e666c 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