Skip to content

[clang] CTAD: implement the missing IsDeducible constraint for alias templates #89358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Basic/TypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down Expand Up @@ -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];
}

Expand Down
10 changes: 9 additions & 1 deletion clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
88 changes: 79 additions & 9 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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>>
Expand Down Expand Up @@ -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:
Expand Down
86 changes: 86 additions & 0 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines +3240 to +3242
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that something you would be willing to do in a follow up patch?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is in my TODO list, but its priority is currently low, so I don't anticipate addressing it in the near future. This seems like a good candidate for a "GoodFirst" issue, so I'm thinking we can file an issue in case someone wants to contribute.

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;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing a consistency check, ie [temp.deduct.type]p1.

Otherwise we would incorrectly accept a match where there would be a non-deduced mismatch.

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
Expand Down Expand Up @@ -3305,6 +3339,58 @@ Sema::DeduceTemplateArguments(VarTemplatePartialSpecializationDecl *Partial,
return ::DeduceTemplateArguments(*this, Partial, TemplateArgs, Info);
}

TemplateDeductionResult
Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType,
Comment on lines +3342 to +3343
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a lot of DeduceTemplateArguments function so I think we want to add a comment here describing what that function does / when it is used.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is public (the documentation and comments are in Sema.h header).

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
Expand Down
20 changes: 15 additions & 5 deletions clang/test/AST/ast-dump-ctad-alias.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 18 additions & 9 deletions clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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}} \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to add code in this PR to improve the diagnostic

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this is a blocker for landing this PR? The current state is acceptable to me, although not ideal. I plan to send out a follow-up patch to address it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I certainly think it's not a good user-facing diagnostic, and i don't think the fix would be difficult
but doing it separately seems reasonable. WDYT @AaronBallman?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to do it in a follow up (this patch is quite large already). Added a FIXME.

// 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 {
Expand Down
16 changes: 10 additions & 6 deletions clang/test/SemaTemplate/deduction-guide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
4 changes: 2 additions & 2 deletions clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading