https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/89358
>From 2385c46a2ae67e0890a7232fdec16b0b92da060b Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Fri, 19 Apr 2024 10:54:12 +0200 Subject: [PATCH 1/3] [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 a27fbed358a60c2..74102f405396816 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 64607b91acbfc95..3b67cc948e84d0b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9598,6 +9598,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 0d2ad980696fcc2..af4e205eeff8032 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 779a41620033dc6..a197a2eabde9130 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 4bda31ba67c02d0..7b848eaf3e36fc8 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 0b6375001f53262..942c7343163e243 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 508a3a5da76a915..8d686ab102fd0e6 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 000000000000000..1a9ba19fb6035df --- /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 c233171e63c8117..8b7c51a5610a909 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 b3f53be02cb73bfe5692dcc2fdf2607f9bde679e Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Mon, 22 Apr 2024 12:48:29 +0200 Subject: [PATCH 2/3] - 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 2b3bafa1c305480..9615f2c6ac8744f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -115,6 +115,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 63e951daec74776..4fe34ee75df50c0 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 a197a2eabde9130..7ccd238f3be1d1a 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 7b848eaf3e36fc8..bcc71b50d505937 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 942c7343163e243..38b2320fc1cf540 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 8d686ab102fd0e6..cbe00181f74b265 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 1a9ba19fb6035df..74753bf60cac25d 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 ff5e39216762faf..4a6d0238fe15922 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 c11f11ba0948c69a3873e59e00c198618e49392a Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Wed, 24 Apr 2024 09:47:27 +0200 Subject: [PATCH 3/3] 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 9615f2c6ac8744f..2b3bafa1c305480 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -115,10 +115,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 4fe34ee75df50c0..63e951daec74776 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 74102f405396816..a27fbed358a60c2 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 eb8b1923152db3e..f0931439332c8a9 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 4dbf678dc395bed..e9f3c55ea13a046 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 af4e205eeff8032..0d2ad980696fcc2 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 7ccd238f3be1d1a..4827227e5b60f37 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 74753bf60cac25d..000000000000000 --- 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 4a6d0238fe15922..80d499ddbe320f7 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 8b7c51a5610a909..a8fc5704398cb48 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -868,7 +868,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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits