[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 Apr 22 03:51:18 PDT 2024
https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/89358
>From 9583811bfa66ff058f5e33012cd77501ce3e5e23 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 1/2] [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
---
clang/include/clang/Basic/TokenKinds.def | 1 +
clang/include/clang/Sema/Sema.h | 9 ++
clang/lib/Parse/ParseExprCXX.cpp | 16 ++--
clang/lib/Sema/SemaExprCXX.cpp | 11 +++
clang/lib/Sema/SemaTemplate.cpp | 70 ++++++++++++---
clang/lib/Sema/SemaTemplateDeduction.cpp | 87 +++++++++++++++++++
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 26 ++++--
.../test/SemaCXX/type-traits-is-deducible.cpp | 47 ++++++++++
clang/www/cxx_status.html | 8 +-
9 files changed, 243 insertions(+), 32 deletions(-)
create mode 100644 clang/test/SemaCXX/type-traits-is-deducible.cpp
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index a27fbed358a60c..74102f40539681 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -537,6 +537,7 @@ TYPE_TRAIT_1(__is_referenceable, IsReferenceable, KEYCXX)
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(__is_deducible, 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 ffc58c681cdcd5..a4470ef0aab280 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9591,6 +9591,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/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 0d2ad980696fcc..af4e205eeff803 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -3906,14 +3906,18 @@ ExprResult Parser::ParseTypeTrait() {
BalancedDelimiterTracker Parens(*this, tok::l_paren);
if (Parens.expectAndConsume())
return ExprError();
-
+ TypeTrait TTKind = TypeTraitFromTokKind(Kind);
SmallVector<ParsedType, 2> Args;
do {
// Parse the next type.
- TypeResult Ty = ParseTypeName(/*SourceRange=*/nullptr,
- getLangOpts().CPlusPlus
- ? DeclaratorContext::TemplateTypeArg
- : DeclaratorContext::TypeName);
+ TypeResult Ty = ParseTypeName(
+ /*SourceRange=*/nullptr,
+ getLangOpts().CPlusPlus
+ // For __is_deducible type trait, the first argument is a template
+ // specification type without template argument lists.
+ ? (TTKind == BTT_IsDeducible ? DeclaratorContext::TemplateArg
+ : DeclaratorContext::TemplateTypeArg)
+ : DeclaratorContext::TypeName);
if (Ty.isInvalid()) {
Parens.skipToEnd();
return ExprError();
@@ -3937,7 +3941,7 @@ ExprResult Parser::ParseTypeTrait() {
SourceLocation EndLoc = Parens.getCloseLocation();
- return Actions.ActOnTypeTrait(TypeTraitFromTokKind(Kind), Loc, Args, EndLoc);
+ return Actions.ActOnTypeTrait(TTKind, Loc, Args, EndLoc);
}
/// ParseArrayTypeTrait - Parse the built-in array type-trait
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 7582cbd75fec05..0833a985b48b88 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6100,6 +6100,17 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
tok::kw___is_pointer_interconvertible_base_of);
return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs);
+ }
+ case BTT_IsDeducible: {
+ if (const auto *TSTToBeDeduced =
+ LhsT->getAs<DeducedTemplateSpecializationType>()) {
+ sema::TemplateDeductionInfo Info(KeyLoc);
+ return Self.DeduceTemplateArgumentsFromType(
+ TSTToBeDeduced->getTemplateName().getAsTemplateDecl(), RhsT,
+ Info) == TemplateDeductionResult::Success;
+ }
+ // FIXME: emit a diagnostic.
+ return false;
}
default: llvm_unreachable("not a BTT");
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 4bda31ba67c02d..7b848eaf3e36fc 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -18,6 +18,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TemplateName.h"
+#include "clang/AST/Type.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticSema.h"
@@ -2780,6 +2781,41 @@ Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *FTD,
return E.getAs<Expr>();
}
+// 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.
+Expr *
+buildAssociatedConstraints(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate,
+ FunctionTemplateDecl *FTD,
+ llvm::ArrayRef<TemplateArgument> TransformedArgs,
+ QualType ReturnType) {
+ auto &Context = SemaRef.Context;
+ TemplateName TN(AliasTemplate);
+ auto TST = Context.getDeducedTemplateSpecializationType(TN, QualType(), true);
+ // Build the IsDeducible constraint.
+ SmallVector<TypeSourceInfo *> IsDeducibleTypeTraitArgs = {
+ Context.getTrivialTypeSourceInfo(TST),
+ Context.getTrivialTypeSourceInfo(ReturnType)};
+ Expr *IsDeducible = TypeTraitExpr::Create(
+ Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),
+ TypeTrait::BTT_IsDeducible, IsDeducibleTypeTraitArgs,
+ AliasTemplate->getLocation(), false);
+
+ // Substitute new template parameters into requires-clause if present.
+ if (auto *TransformedRC =
+ transformRequireClause(SemaRef, FTD, TransformedArgs)) {
+ auto Conjunction = SemaRef.BuildBinOp(
+ SemaRef.getCurScope(), SourceLocation{}, BinaryOperatorKind::BO_LAnd,
+ TransformedRC, IsDeducible);
+ if (Conjunction.isInvalid())
+ return nullptr;
+ return Conjunction.get();
+ }
+ return IsDeducible;
+}
+
std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>>
getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) {
// Unwrap the sugared ElaboratedType.
@@ -3006,6 +3042,18 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
if (auto *FPrime = SemaRef.InstantiateFunctionDeclaration(
F, TemplateArgListForBuildingFPrime, AliasTemplate->getLocation(),
Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
+ Expr *RequireClause = buildAssociatedConstraints(
+ SemaRef, AliasTemplate, F, TemplateArgsForBuildingFPrime,
+ FPrime->getReturnType());
+ if (!RequireClause)
+ continue;
+ auto *FPrimeTemplateParamList = TemplateParameterList::Create(
+ Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
+ AliasTemplate->getTemplateParameters()->getLAngleLoc(),
+ FPrimeTemplateParams,
+ AliasTemplate->getTemplateParameters()->getRAngleLoc(),
+ RequireClause);
+
auto *GG = cast<CXXDeductionGuideDecl>(FPrime);
// Substitute new template parameters into requires-clause if present.
Expr *RequiresClause =
@@ -3073,16 +3121,6 @@ FunctionTemplateDecl *DeclareAggregateDeductionGuideForTypeAlias(
SemaRef.Context.getInjectedTemplateArg(NewParam));
TransformedTemplateParams.push_back(NewParam);
}
- // FIXME: implement the is_deducible constraint per C++
- // [over.match.class.deduct]p3.3.
- Expr *TransformedRequiresClause = transformRequireClause(
- SemaRef, RHSDeductionGuide, TransformedTemplateArgs);
- auto *TransformedTemplateParameterList = TemplateParameterList::Create(
- SemaRef.Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
- AliasTemplate->getTemplateParameters()->getLAngleLoc(),
- TransformedTemplateParams,
- AliasTemplate->getTemplateParameters()->getRAngleLoc(),
- TransformedRequiresClause);
auto *TransformedTemplateArgList = TemplateArgumentList::CreateCopy(
SemaRef.Context, TransformedTemplateArgs);
@@ -3090,6 +3128,18 @@ FunctionTemplateDecl *DeclareAggregateDeductionGuideForTypeAlias(
RHSDeductionGuide, TransformedTemplateArgList,
AliasTemplate->getLocation(),
Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
+ Expr *RequireClause = buildAssociatedConstraints(
+ SemaRef, AliasTemplate, RHSDeductionGuide, TransformedTemplateArgs,
+ TransformedDeductionGuide->getReturnType());
+ if (!RequireClause)
+ return nullptr;
+ auto *TransformedTemplateParameterList = TemplateParameterList::Create(
+ SemaRef.Context,
+ AliasTemplate->getTemplateParameters()->getTemplateLoc(),
+ AliasTemplate->getTemplateParameters()->getLAngleLoc(),
+ TransformedTemplateParams,
+ AliasTemplate->getTemplateParameters()->getRAngleLoc(), RequireClause);
+
auto *GD =
llvm::dyn_cast<clang::CXXDeductionGuideDecl>(TransformedDeductionGuide);
FunctionTemplateDecl *Result = buildDeductionGuide(
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 0b6375001f5326..942c7343163e24 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3139,6 +3139,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
@@ -3207,6 +3241,59 @@ 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 {
+ // FIXME: emit a diagnostic, we only only support alias and class templates.
+ return TemplateDeductionResult::Invalid;
+ }
+
+ // 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/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 508a3a5da76a91..8d686ab102fd0e 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(Bar, Foo<int, 4UL>)' evaluated to false}}
-// 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 T = 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,15 @@ 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>;
+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/SemaCXX/type-traits-is-deducible.cpp b/clang/test/SemaCXX/type-traits-is-deducible.cpp
new file mode 100644
index 00000000000000..1a9ba19fb6035d
--- /dev/null
+++ b/clang/test/SemaCXX/type-traits-is-deducible.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+// expected-no-diagnostics
+
+template<typename T>
+struct Foo {};
+static_assert(__is_deducible(Foo, Foo<int>));
+static_assert(!__is_deducible(Foo, int));
+
+template <class T>
+using AFoo1 = Foo<T*>;
+static_assert(__is_deducible(AFoo1, Foo<int*>));
+static_assert(!__is_deducible(AFoo1, Foo<int>));
+
+template <class T>
+using AFoo2 = Foo<int>;
+static_assert(!__is_deducible(AFoo2, Foo<int>));
+
+// default template argument counts.
+template <class T = double>
+using AFoo3 = Foo<int>;
+static_assert(__is_deducible(AFoo3, Foo<int>));
+
+
+template <int N>
+struct Bar { int k = N; };
+static_assert(__is_deducible(Bar, Bar<1>));
+
+template <int N>
+using ABar1 = Bar<N>;
+static_assert(__is_deducible(ABar1, Bar<3>));
+template <int N>
+using ABar2 = Bar<1>;
+static_assert(!__is_deducible(ABar2, Bar<1>));
+
+
+template <typename T>
+class Forward;
+static_assert(__is_deducible(Forward, Forward<int>));
+template <typename T>
+using AForward = Forward<T>;
+static_assert(__is_deducible(AForward, Forward<int>));
+
+
+template <class T, T N>
+using AArrary = int[N];
+static_assert (__is_deducible(AArrary, int[42]));
+static_assert (!__is_deducible(AArrary, double[42]));
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index c233171e63c811..8b7c51a5610a90 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -868,13 +868,7 @@ <h2 id="cxx20">C++20 implementation status</h2>
<tr>
<td>Class template argument deduction for alias templates</td>
<td><a href="https://wg21.link/p1814r0">P1814R0</a></td>
- <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.
- </details>
- </td>
+ <td class="partial" align="center">Clang 19 (Partial)</td>
</tr>
<tr>
<td>Permit conversions to arrays of unknown bound</td>
>From dbf9d54c0176d8f87dd6c44142546cc4454b7e9a Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Mon, 22 Apr 2024 12:48:29 +0200
Subject: [PATCH 2/2] - rebase to main - add release note for __is_deducible -
implement diagnostics for bad argument types for __is_deducible
---
clang/docs/ReleaseNotes.rst | 4 ++++
clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++++
clang/lib/Sema/SemaExprCXX.cpp | 4 +++-
clang/lib/Sema/SemaTemplate.cpp | 13 -------------
clang/lib/Sema/SemaTemplateDeduction.cpp | 3 +--
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 2 +-
clang/test/SemaCXX/type-traits-is-deducible.cpp | 10 +++++++++-
clang/test/SemaTemplate/deduction-guide.cpp | 12 +++++++-----
8 files changed, 29 insertions(+), 23 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c44f238e33846b..d2136fc3ea17cc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -114,6 +114,10 @@ C++20 Feature Support
to update the ``__cpp_concepts`` macro to `202002L`. This enables
``<expected>`` from libstdc++ to work correctly with Clang.
+- Implemented the `__is_deducible` builtin to check if the template arguments of
+ a class/alias template can be deduced from a specific type,
+ [over.match.class.deduct]p4.
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1bbe76ff6bd2ac..371f00d0c49636 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9156,6 +9156,10 @@ def note_inequality_comparison_to_or_assign : Note<
def err_incomplete_type_used_in_type_trait_expr : Error<
"incomplete type %0 used in type trait expression">;
+def err_deducible_non_class_or_alias_types : Error<
+ "%0 is not a class or alias template; __is_deducible only supports class or "
+ "alias templates">;
+
// C++20 constinit and require_constant_initialization attribute
def warn_cxx20_compat_constinit : Warning<
"'constinit' specifier is incompatible with C++ standards before C++20">,
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 0833a985b48b88..e0c4c5858e9aa2 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6109,7 +6109,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
TSTToBeDeduced->getTemplateName().getAsTemplateDecl(), RhsT,
Info) == TemplateDeductionResult::Success;
}
- // FIXME: emit a diagnostic.
+ Self.Diag(Lhs->getTypeLoc().getBeginLoc(),
+ diag::err_deducible_non_class_or_alias_types)
+ << LhsT << Lhs->getTypeLoc().getSourceRange();
return false;
}
default: llvm_unreachable("not a BTT");
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 7b848eaf3e36fc..bcc71b50d50593 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3055,19 +3055,6 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
RequireClause);
auto *GG = cast<CXXDeductionGuideDecl>(FPrime);
- // Substitute new template parameters into requires-clause if present.
- Expr *RequiresClause =
- transformRequireClause(SemaRef, F, TemplateArgsForBuildingFPrime);
- // FIXME: implement the is_deducible constraint per C++
- // [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.
- auto *FPrimeTemplateParamList = TemplateParameterList::Create(
- Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
- AliasTemplate->getTemplateParameters()->getLAngleLoc(),
- FPrimeTemplateParams,
- AliasTemplate->getTemplateParameters()->getRAngleLoc(),
- /*RequiresClause=*/RequiresClause);
buildDeductionGuide(SemaRef, AliasTemplate, FPrimeTemplateParamList,
GG->getCorrespondingConstructor(),
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 942c7343163e24..38b2320fc1cf54 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3256,8 +3256,7 @@ Sema::DeduceTemplateArgumentsFromType(TemplateDecl *TD, QualType FromType,
->getUnderlyingType()
.getCanonicalType();
} else {
- // FIXME: emit a diagnostic, we only only support alias and class templates.
- return TemplateDeductionResult::Invalid;
+ assert(false && "Expected a class or alias template");
}
// Unevaluated SFINAE context.
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 8d686ab102fd0e..cbe00181f74b26 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -111,7 +111,7 @@ struct Foo {
template <typename X, int Y>
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(Bar, Foo<int, 4UL>)' evaluated to false}}
+ // expected-note {{because '__is_deducible}}
Bar s = {{1}}; // expected-error {{no viable constructor or deduction guide }}
diff --git a/clang/test/SemaCXX/type-traits-is-deducible.cpp b/clang/test/SemaCXX/type-traits-is-deducible.cpp
index 1a9ba19fb6035d..74753bf60cac25 100644
--- a/clang/test/SemaCXX/type-traits-is-deducible.cpp
+++ b/clang/test/SemaCXX/type-traits-is-deducible.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
-// expected-no-diagnostics
template<typename T>
struct Foo {};
@@ -45,3 +44,12 @@ template <class T, T N>
using AArrary = int[N];
static_assert (__is_deducible(AArrary, int[42]));
static_assert (!__is_deducible(AArrary, double[42]));
+
+// error cases
+bool e1 = __is_deducible(int, int); // expected-error {{'int' is not a class or alias template; __is_deducible only supports class or alias templates}}
+template<typename T> T func();
+bool e2 = __is_deducible(func, int); // expected-error {{type name requires a specifier or qualifier}} \
+ expected-error {{type-id cannot have a name}}
+template<typename T> T var = 1;
+bool e3 = __is_deducible(var, int); // expected-error {{type name requires a specifier or qualifier}} \
+ expected-error {{type-id cannot have a name}}
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index ff5e39216762fa..4a6d0238fe1592 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -275,11 +275,13 @@ 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: |-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: |-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
More information about the cfe-commits
mailing list