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

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Tue May 7 03:22:26 PDT 2024


https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/89358

>From bf6acda6c7cb9a08b82b149c0df38d90e395f9e1 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/4] [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 a27fbed358a60..74102f4053968 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 a80ac6dbc7613..6f9a31bbbaf60 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/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 0d2ad980696fc..af4e205eeff80 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 c1cb03e4ec7ae..222e8babdfaaa 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6116,6 +6116,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 989f3995ca599..7c292a295aacc 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"
@@ -2774,6 +2775,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.
@@ -3000,6 +3036,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 =
@@ -3067,16 +3115,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);
 
@@ -3084,6 +3122,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 9f9e442282717..072f16e5f5793 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3239,6 +3239,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
@@ -3307,6 +3341,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 508a3a5da76a9..8d686ab102fd0 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 0000000000000..1a9ba19fb6035
--- /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 b39cfbd76df25..cb93642d41572 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -875,13 +875,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 c524b104cb845dc50415c00ea2a750b999ecd173 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/4] - 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 3a68a6d5efe5f..09719c9dd1494 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -168,6 +168,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 fbb107679d94b..e6b40d6ebd875 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 222e8babdfaaa..6e9d85619daf7 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6125,7 +6125,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 7c292a295aacc..223ea2e96a30a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3049,19 +3049,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 072f16e5f5793..1dd3175a7ea18 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3356,8 +3356,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 8d686ab102fd0..cbe00181f74b2 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 1a9ba19fb6035..74753bf60cac2 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 ff5e39216762f..4a6d0238fe159 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

>From 552156ff7e3230940d4a53bf266267990aee4af8 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Wed, 24 Apr 2024 09:47:27 +0200
Subject: [PATCH 3/4] Don't expose __is_deducible trait.

---
 clang/docs/ReleaseNotes.rst                   |  4 --
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 --
 clang/include/clang/Basic/TokenKinds.def      |  1 -
 clang/include/clang/Basic/TypeTraits.h        |  7 ++-
 clang/lib/Basic/TypeTraits.cpp                |  9 +++
 clang/lib/Parse/ParseExprCXX.cpp              | 16 ++----
 clang/lib/Sema/SemaExprCXX.cpp                |  4 +-
 .../test/SemaCXX/type-traits-is-deducible.cpp | 55 -------------------
 clang/test/SemaTemplate/deduction-guide.cpp   |  4 +-
 clang/www/cxx_status.html                     |  8 ++-
 10 files changed, 32 insertions(+), 80 deletions(-)
 delete mode 100644 clang/test/SemaCXX/type-traits-is-deducible.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 09719c9dd1494..3a68a6d5efe5f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -168,10 +168,6 @@ 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 e6b40d6ebd875..fbb107679d94b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9156,10 +9156,6 @@ 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/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 74102f4053968..a27fbed358a60 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -537,7 +537,6 @@ 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/Basic/TypeTraits.h b/clang/include/clang/Basic/TypeTraits.h
index eb8b1923152db..f0931439332c8 100644
--- a/clang/include/clang/Basic/TypeTraits.h
+++ b/clang/include/clang/Basic/TypeTraits.h
@@ -26,8 +26,13 @@ enum TypeTrait {
 #include "clang/Basic/TokenKinds.def"
   ,
 #define TYPE_TRAIT_2(Spelling, Name, Key) BTT_##Name,
+  // IsDeducible is only used internally by clang for CTAD implementation and
+  // is not exposed to users.
+  TYPE_TRAIT_2(/**/, IsDeducible, KEYCXX)
 #include "clang/Basic/TokenKinds.def"
-  BTT_Last = UTT_Last // BTT_Last == last BTT_XX in the enum.
+  // +1 for the IsDeducible enumerator.
+  BTT_Last = UTT_Last + 1 // BTT_Last == last BTT_XX in the enum.
+
 #define TYPE_TRAIT_2(Spelling, Name, Key) +1
 #include "clang/Basic/TokenKinds.def"
   ,
diff --git a/clang/lib/Basic/TypeTraits.cpp b/clang/lib/Basic/TypeTraits.cpp
index 4dbf678dc395b..e9f3c55ea13a0 100644
--- a/clang/lib/Basic/TypeTraits.cpp
+++ b/clang/lib/Basic/TypeTraits.cpp
@@ -19,6 +19,9 @@ static constexpr const char *TypeTraitNames[] = {
 #define TYPE_TRAIT_1(Spelling, Name, Key) #Name,
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_2(Spelling, Name, Key) #Name,
+    // IsDeducible is only used internally by clang for CTAD implementation and
+    // is not exposed to users.
+    TYPE_TRAIT_2(/**/, IsDeducible, KEYCXX)
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_N(Spelling, Name, Key) #Name,
 #include "clang/Basic/TokenKinds.def"
@@ -28,6 +31,9 @@ static constexpr const char *TypeTraitSpellings[] = {
 #define TYPE_TRAIT_1(Spelling, Name, Key) #Spelling,
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_2(Spelling, Name, Key) #Spelling,
+    // __is_deducible is only used internally by clang for CTAD implementation
+    // and is not exposed to users.
+    TYPE_TRAIT_2(__is_deducible, /**/, KEYCXX)
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_N(Spelling, Name, Key) #Spelling,
 #include "clang/Basic/TokenKinds.def"
@@ -59,6 +65,9 @@ static constexpr const unsigned TypeTraitArities[] = {
 #define TYPE_TRAIT_1(Spelling, Name, Key) 1,
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_2(Spelling, Name, Key) 2,
+    // IsDeducible is only used internally by clang for CTAD implementation and
+    // is not exposed to users.
+    TYPE_TRAIT_2(/**/, IsDeducible, KEYCXX)
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_N(Spelling, Name, Key) 0,
 #include "clang/Basic/TokenKinds.def"
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index af4e205eeff80..0d2ad980696fc 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -3906,18 +3906,14 @@ 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
-            // 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);
+    TypeResult Ty = ParseTypeName(/*SourceRange=*/nullptr,
+                                  getLangOpts().CPlusPlus
+                                      ? DeclaratorContext::TemplateTypeArg
+                                      : DeclaratorContext::TypeName);
     if (Ty.isInvalid()) {
       Parens.skipToEnd();
       return ExprError();
@@ -3941,7 +3937,7 @@ ExprResult Parser::ParseTypeTrait() {
 
   SourceLocation EndLoc = Parens.getCloseLocation();
 
-  return Actions.ActOnTypeTrait(TTKind, Loc, Args, EndLoc);
+  return Actions.ActOnTypeTrait(TypeTraitFromTokKind(Kind), 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 6e9d85619daf7..ce9d370655b1a 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6125,9 +6125,7 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
                  TSTToBeDeduced->getTemplateName().getAsTemplateDecl(), RhsT,
                  Info) == TemplateDeductionResult::Success;
     }
-    Self.Diag(Lhs->getTypeLoc().getBeginLoc(),
-              diag::err_deducible_non_class_or_alias_types)
-        << LhsT << Lhs->getTypeLoc().getSourceRange();
+    assert("Expect to see DeducedTemplateSpecializationType!");
     return false;
   }
     default: llvm_unreachable("not a BTT");
diff --git a/clang/test/SemaCXX/type-traits-is-deducible.cpp b/clang/test/SemaCXX/type-traits-is-deducible.cpp
deleted file mode 100644
index 74753bf60cac2..0000000000000
--- a/clang/test/SemaCXX/type-traits-is-deducible.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s 
-
-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]));
-
-// 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 4a6d0238fe159..80d499ddbe320 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -282,7 +282,9 @@ using AFoo = Foo<G<U>>;
 // 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: |   |-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 cb93642d41572..8493f18d9d33c 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -875,7 +875,13 @@ <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">Clang 19 (Partial)</td>
+      <td class="partial" align="center">
+        <details>
+          <summary>Clang 19 (Partial)</summary>
+          This feature has been initially completed, but the feature macro
+          __cpp_deduction_guides has not been updated.
+        </details>
+      </td>
     </tr>
     <tr>
       <td>Permit conversions to arrays of unknown bound</td>

>From 5ef6c00fec3dcbfd0721cf897d2434993ec3f5f7 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Tue, 7 May 2024 12:15:00 +0200
Subject: [PATCH 4/4] Refine the implementation of hiding __is_deducible type
 trait.

Apply approach 3.
---
 clang/include/clang/Basic/TokenKinds.def |  3 +++
 clang/include/clang/Basic/TypeTraits.h   |  6 +-----
 clang/lib/Basic/TypeTraits.cpp           | 19 ++++++++++---------
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index a27fbed358a60..1b4b756e6e5ee 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -537,6 +537,9 @@ 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)
+// 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/Basic/TypeTraits.h b/clang/include/clang/Basic/TypeTraits.h
index f0931439332c8..b4b44c0baa3a2 100644
--- a/clang/include/clang/Basic/TypeTraits.h
+++ b/clang/include/clang/Basic/TypeTraits.h
@@ -26,12 +26,8 @@ enum TypeTrait {
 #include "clang/Basic/TokenKinds.def"
   ,
 #define TYPE_TRAIT_2(Spelling, Name, Key) BTT_##Name,
-  // IsDeducible is only used internally by clang for CTAD implementation and
-  // is not exposed to users.
-  TYPE_TRAIT_2(/**/, IsDeducible, KEYCXX)
 #include "clang/Basic/TokenKinds.def"
-  // +1 for the IsDeducible enumerator.
-  BTT_Last = UTT_Last + 1 // BTT_Last == last BTT_XX in the enum.
+  BTT_Last = UTT_Last
 
 #define TYPE_TRAIT_2(Spelling, Name, Key) +1
 #include "clang/Basic/TokenKinds.def"
diff --git a/clang/lib/Basic/TypeTraits.cpp b/clang/lib/Basic/TypeTraits.cpp
index e9f3c55ea13a0..8d6794223ccaf 100644
--- a/clang/lib/Basic/TypeTraits.cpp
+++ b/clang/lib/Basic/TypeTraits.cpp
@@ -13,15 +13,13 @@
 #include "clang/Basic/TypeTraits.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <cassert>
+#include <cstring>
 using namespace clang;
 
 static constexpr const char *TypeTraitNames[] = {
 #define TYPE_TRAIT_1(Spelling, Name, Key) #Name,
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_2(Spelling, Name, Key) #Name,
-    // IsDeducible is only used internally by clang for CTAD implementation and
-    // is not exposed to users.
-    TYPE_TRAIT_2(/**/, IsDeducible, KEYCXX)
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_N(Spelling, Name, Key) #Name,
 #include "clang/Basic/TokenKinds.def"
@@ -31,9 +29,6 @@ static constexpr const char *TypeTraitSpellings[] = {
 #define TYPE_TRAIT_1(Spelling, Name, Key) #Spelling,
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_2(Spelling, Name, Key) #Spelling,
-    // __is_deducible is only used internally by clang for CTAD implementation
-    // and is not exposed to users.
-    TYPE_TRAIT_2(__is_deducible, /**/, KEYCXX)
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_N(Spelling, Name, Key) #Spelling,
 #include "clang/Basic/TokenKinds.def"
@@ -65,9 +60,6 @@ static constexpr const unsigned TypeTraitArities[] = {
 #define TYPE_TRAIT_1(Spelling, Name, Key) 1,
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_2(Spelling, Name, Key) 2,
-    // IsDeducible is only used internally by clang for CTAD implementation and
-    // is not exposed to users.
-    TYPE_TRAIT_2(/**/, IsDeducible, KEYCXX)
 #include "clang/Basic/TokenKinds.def"
 #define TYPE_TRAIT_N(Spelling, Name, Key) 0,
 #include "clang/Basic/TokenKinds.def"
@@ -90,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];
 }
 



More information about the cfe-commits mailing list