[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