[clang] 632dd6a - [Clang] Implements CTAD for aggregates P1816R0 and P2082R1
Yuanfang Chen via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 29 14:23:15 PDT 2023
Author: Yuanfang Chen
Date: 2023-06-29T14:22:24-07:00
New Revision: 632dd6a4ca0036009febd55d95e4e1f93eeb412c
URL: https://github.com/llvm/llvm-project/commit/632dd6a4ca0036009febd55d95e4e1f93eeb412c
DIFF: https://github.com/llvm/llvm-project/commit/632dd6a4ca0036009febd55d95e4e1f93eeb412c.diff
LOG: [Clang] Implements CTAD for aggregates P1816R0 and P2082R1
Differential Revision: https://reviews.llvm.org/D139837
Added:
clang/test/SemaTemplate/aggregate-deduction-candidate.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/DeclBase.h
clang/include/clang/AST/DeclCXX.h
clang/include/clang/Sema/Sema.h
clang/include/clang/Sema/TemplateDeduction.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/Decl.cpp
clang/lib/AST/DeclCXX.cpp
clang/lib/Frontend/FrontendActions.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateDeduction.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/unittests/AST/ASTImporterTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 03c27a207d5e1b..3afb4b1edb2dbd 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -112,6 +112,8 @@ C++20 Feature Support
- Clang now supports `requires cplusplus20` for module maps.
- Implemented missing parts of `P2002R1: Consistent comparison operators <https://wg21.link/P2002R1>`_
- Clang now defines `__cpp_consteval` macro.
+- Implemented `P1816R0: <https://wg21.link/p1816r0>`_ and `P2082R1: <https://wg21.link/p2082r1>`_,
+ which allows CTAD for aggregates.
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index eea8a111ab774f..1b99709ca90d99 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -1382,6 +1382,13 @@ class DeclContextLookupResult {
}
};
+/// Only used by CXXDeductionGuideDecl.
+enum class DeductionCandidate : unsigned char {
+ Normal,
+ Copy,
+ Aggregate,
+};
+
/// DeclContext - This is used only as base class of specific decl types that
/// can act as declaration contexts. These decls are (only the top classes
/// that directly derive from DeclContext are mentioned, not their subclasses):
@@ -1620,10 +1627,10 @@ class DeclContext {
/// Stores the bits used by FunctionDecl.
/// If modified NumFunctionDeclBits and the accessor
/// methods in FunctionDecl and CXXDeductionGuideDecl
- /// (for IsCopyDeductionCandidate) should be updated appropriately.
+ /// (for DeductionCandidateKind) should be updated appropriately.
class FunctionDeclBitfields {
friend class FunctionDecl;
- /// For IsCopyDeductionCandidate
+ /// For DeductionCandidateKind
friend class CXXDeductionGuideDecl;
/// For the bits in DeclContextBitfields.
uint64_t : NumDeclContextBits;
@@ -1678,10 +1685,10 @@ class DeclContext {
/// function using attribute 'target'.
uint64_t IsMultiVersion : 1;
- /// [C++17] Only used by CXXDeductionGuideDecl. Indicates that
- /// the Deduction Guide is the implicitly generated 'copy
- /// deduction candidate' (is used during overload resolution).
- uint64_t IsCopyDeductionCandidate : 1;
+ /// Only used by CXXDeductionGuideDecl. Indicates the kind
+ /// of the Deduction Guide that is implicitly generated
+ /// (used during overload resolution).
+ uint64_t DeductionCandidateKind : 2;
/// Store the ODRHash after first calculation.
uint64_t HasODRHash : 1;
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index b8e5224a0f2251..f4322e1e00f0c6 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1942,13 +1942,13 @@ class CXXDeductionGuideDecl : public FunctionDecl {
ExplicitSpecifier ES,
const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation,
- CXXConstructorDecl *Ctor)
+ CXXConstructorDecl *Ctor, DeductionCandidate Kind)
: FunctionDecl(CXXDeductionGuide, C, DC, StartLoc, NameInfo, T, TInfo,
SC_None, false, false, ConstexprSpecKind::Unspecified),
Ctor(Ctor), ExplicitSpec(ES) {
if (EndLocation.isValid())
setRangeEnd(EndLocation);
- setIsCopyDeductionCandidate(false);
+ setDeductionCandidateKind(Kind);
}
CXXConstructorDecl *Ctor;
@@ -1963,7 +1963,8 @@ class CXXDeductionGuideDecl : public FunctionDecl {
Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation,
- CXXConstructorDecl *Ctor = nullptr);
+ CXXConstructorDecl *Ctor = nullptr,
+ DeductionCandidate Kind = DeductionCandidate::Normal);
static CXXDeductionGuideDecl *CreateDeserialized(ASTContext &C, unsigned ID);
@@ -1980,16 +1981,15 @@ class CXXDeductionGuideDecl : public FunctionDecl {
/// Get the constructor from which this deduction guide was generated, if
/// this is an implicit deduction guide.
- CXXConstructorDecl *getCorrespondingConstructor() const {
- return Ctor;
- }
+ CXXConstructorDecl *getCorrespondingConstructor() const { return Ctor; }
- void setIsCopyDeductionCandidate(bool isCDC = true) {
- FunctionDeclBits.IsCopyDeductionCandidate = isCDC;
+ void setDeductionCandidateKind(DeductionCandidate K) {
+ FunctionDeclBits.DeductionCandidateKind = static_cast<unsigned char>(K);
}
- bool isCopyDeductionCandidate() const {
- return FunctionDeclBits.IsCopyDeductionCandidate;
+ DeductionCandidate getDeductionCandidateKind() const {
+ return static_cast<DeductionCandidate>(
+ FunctionDeclBits.DeductionCandidateKind);
}
// Implement isa/cast/dyncast/etc.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4bf05b33bfb3ec..4cb02f31b6514f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3996,7 +3996,8 @@ class Sema final {
bool AllowExplicitConversion = false,
ADLCallKind IsADLCandidate = ADLCallKind::NotADL,
ConversionSequenceList EarlyConversions = std::nullopt,
- OverloadCandidateParamOrder PO = {});
+ OverloadCandidateParamOrder PO = {},
+ bool AggregateCandidateDeduction = false);
void AddFunctionCandidates(const UnresolvedSetImpl &Functions,
ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet,
@@ -4037,7 +4038,8 @@ class Sema final {
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false,
bool PartialOverloading = false, bool AllowExplicit = true,
ADLCallKind IsADLCandidate = ADLCallKind::NotADL,
- OverloadCandidateParamOrder PO = {});
+ OverloadCandidateParamOrder PO = {},
+ bool AggregateCandidateDeduction = false);
bool CheckNonDependentConversions(
FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType> ParamTypes,
ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
@@ -9112,7 +9114,7 @@ class Sema final {
FunctionTemplateDecl *FunctionTemplate,
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info,
- bool PartialOverloading,
+ bool PartialOverloading, bool AggregateDeductionCandidate,
llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent);
TemplateDeductionResult
@@ -9172,10 +9174,16 @@ class Sema final {
/// not already done so.
void DeclareImplicitDeductionGuides(TemplateDecl *Template,
SourceLocation Loc);
+ FunctionTemplateDecl *DeclareImplicitDeductionGuideFromInitList(
+ TemplateDecl *Template, MutableArrayRef<QualType> ParamTypes,
+ SourceLocation Loc);
+ llvm::DenseMap<unsigned, CXXDeductionGuideDecl *>
+ AggregateDeductionCandidates;
QualType DeduceTemplateSpecializationFromInitializer(
TypeSourceInfo *TInfo, const InitializedEntity &Entity,
- const InitializationKind &Kind, MultiExprArg Init);
+ const InitializationKind &Kind, MultiExprArg Init,
+ ParenListExpr *PL = nullptr);
QualType deduceVarTypeFromInitializer(VarDecl *VDecl, DeclarationName Name,
QualType Type, TypeSourceInfo *TSI,
@@ -9355,7 +9363,10 @@ class Sema final {
/// Memoization means we are _not_ instantiating a template because
/// it is already instantiated (but we entered a context where we
/// would have had to if it was not already instantiated).
- Memoization
+ Memoization,
+
+ /// We are building deduction guides for a class.
+ BuildingDeductionGuides,
} Kind;
/// Was the enclosing context a non-instantiation SFINAE context?
@@ -9668,6 +9679,13 @@ class Sema final {
const RequiresExpr *E,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange);
+
+ struct BuildingDeductionGuidesTag {};
+ /// \brief Note that we are building deduction guides.
+ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ TemplateDecl *Entity, BuildingDeductionGuidesTag,
+ SourceRange InstantiationRange = SourceRange());
+
/// Note that we have finished instantiating this template.
void Clear();
diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h
index 9d860a8949d76f..85691c66a04433 100644
--- a/clang/include/clang/Sema/TemplateDeduction.h
+++ b/clang/include/clang/Sema/TemplateDeduction.h
@@ -234,6 +234,13 @@ class TemplateDeductionInfo {
///
diff erent argument type from its substituted parameter type.
unsigned CallArgIndex = 0;
+ // C++20 [over.match.class.deduct]p5.2:
+ // During template argument deduction for the aggregate deduction
+ // candidate, the number of elements in a trailing parameter pack is only
+ // deduced from the number of remaining function arguments if it is not
+ // otherwise deduced.
+ bool AggregateDeductionCandidateHasMismatchedArity = false;
+
/// Information on packs that we're currently expanding.
///
/// FIXME: This should be kept internal to SemaTemplateDeduction.
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 183104d16306d4..1f4e2b27fc4a2d 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -3699,7 +3699,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
NameInfo, T, TInfo, ToEndLoc, Ctor))
return ToFunction;
cast<CXXDeductionGuideDecl>(ToFunction)
- ->setIsCopyDeductionCandidate(Guide->isCopyDeductionCandidate());
+ ->setDeductionCandidateKind(Guide->getDeductionCandidateKind());
} else {
if (GetImportedOrCreateDecl(
ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart,
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 517df05f0b15be..d19616d4368524 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3009,7 +3009,8 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
FunctionDeclBits.HasSkippedBody = false;
FunctionDeclBits.WillHaveBody = false;
FunctionDeclBits.IsMultiVersion = false;
- FunctionDeclBits.IsCopyDeductionCandidate = false;
+ FunctionDeclBits.DeductionCandidateKind =
+ static_cast<unsigned char>(DeductionCandidate::Normal);
FunctionDeclBits.HasODRHash = false;
FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false;
if (TrailingRequiresClause)
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 56c40e18ca28fd..bbd7901dc8e824 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2112,21 +2112,21 @@ ExplicitSpecifier ExplicitSpecifier::getFromDecl(FunctionDecl *Function) {
}
}
-CXXDeductionGuideDecl *
-CXXDeductionGuideDecl::Create(ASTContext &C, DeclContext *DC,
- SourceLocation StartLoc, ExplicitSpecifier ES,
- const DeclarationNameInfo &NameInfo, QualType T,
- TypeSourceInfo *TInfo, SourceLocation EndLocation,
- CXXConstructorDecl *Ctor) {
+CXXDeductionGuideDecl *CXXDeductionGuideDecl::Create(
+ ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
+ ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T,
+ TypeSourceInfo *TInfo, SourceLocation EndLocation, CXXConstructorDecl *Ctor,
+ DeductionCandidate Kind) {
return new (C, DC) CXXDeductionGuideDecl(C, DC, StartLoc, ES, NameInfo, T,
- TInfo, EndLocation, Ctor);
+ TInfo, EndLocation, Ctor, Kind);
}
CXXDeductionGuideDecl *CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C,
unsigned ID) {
return new (C, ID) CXXDeductionGuideDecl(
C, nullptr, SourceLocation(), ExplicitSpecifier(), DeclarationNameInfo(),
- QualType(), nullptr, SourceLocation(), nullptr);
+ QualType(), nullptr, SourceLocation(), nullptr,
+ DeductionCandidate::Normal);
}
RequiresExprBodyDecl *RequiresExprBodyDecl::Create(
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index a6846b9c952071..8a4a4cf6823df0 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -412,6 +412,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
return "MarkingClassDllexported";
case CodeSynthesisContext::BuildingBuiltinDumpStructCall:
return "BuildingBuiltinDumpStructCall";
+ case CodeSynthesisContext::BuildingDeductionGuides:
+ return "BuildingDeductionGuides";
}
return "";
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d187e222b644f9..9e54b6c3ed0b32 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -12700,10 +12700,9 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl,
if (Init)
DeduceInits = Init;
- if (DirectInit) {
- if (auto *PL = dyn_cast_or_null<ParenListExpr>(Init))
- DeduceInits = PL->exprs();
- }
+ auto *PL = dyn_cast_if_present<ParenListExpr>(Init);
+ if (DirectInit && PL)
+ DeduceInits = PL->exprs();
if (isa<DeducedTemplateSpecializationType>(Deduced)) {
assert(VDecl && "non-auto type for init capture deduction?");
@@ -12713,7 +12712,7 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl,
// FIXME: Initialization should not be taking a mutable list of inits.
SmallVector<Expr*, 8> InitsCopy(DeduceInits.begin(), DeduceInits.end());
return DeduceTemplateSpecializationFromInitializer(TSI, Entity, Kind,
- InitsCopy);
+ InitsCopy, PL);
}
if (DirectInit) {
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index f9fd9648c01cae..90c1bfa8f6d1fa 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -25,8 +25,10 @@
#include "clang/Sema/Lookup.h"
#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
@@ -305,6 +307,7 @@ class InitListChecker {
bool InOverloadResolution;
InitListExpr *FullyStructuredList = nullptr;
NoInitExpr *DummyExpr = nullptr;
+ SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr;
NoInitExpr *getDummyInit() {
if (!DummyExpr)
@@ -354,7 +357,7 @@ class InitListChecker {
unsigned &StructuredIndex);
void CheckStructUnionTypes(const InitializedEntity &Entity,
InitListExpr *IList, QualType DeclType,
- CXXRecordDecl::base_class_range Bases,
+ CXXRecordDecl::base_class_const_range Bases,
RecordDecl::field_iterator Field,
bool SubobjectIsDesignatorContext, unsigned &Index,
InitListExpr *StructuredList,
@@ -391,6 +394,7 @@ class InitListChecker {
unsigned ExpectedNumInits);
int numArrayElements(QualType DeclType);
int numStructUnionElements(QualType DeclType);
+ static RecordDecl *getRecordDecl(QualType DeclType);
ExprResult PerformEmptyInit(SourceLocation Loc,
const InitializedEntity &Entity);
@@ -493,9 +497,19 @@ class InitListChecker {
SourceLocation Loc);
public:
+ InitListChecker(
+ Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
+ bool VerifyOnly, bool TreatUnavailableAsInvalid,
+ bool InOverloadResolution = false,
+ SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr);
InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL,
- QualType &T, bool VerifyOnly, bool TreatUnavailableAsInvalid,
- bool InOverloadResolution = false);
+ QualType &T,
+ SmallVectorImpl<QualType> &AggrDeductionCandidateParamTypes)
+ : InitListChecker(S, Entity, IL, T, /*VerifyOnly=*/true,
+ /*TreatUnavailableAsInvalid=*/false,
+ /*InOverloadResolution=*/false,
+ &AggrDeductionCandidateParamTypes){};
+
bool HadError() { return hadError; }
// Retrieves the fully-structured initializer list used for
@@ -955,18 +969,19 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity,
static bool hasAnyDesignatedInits(const InitListExpr *IL) {
for (const Stmt *Init : *IL)
- if (Init && isa<DesignatedInitExpr>(Init))
+ if (isa_and_nonnull<DesignatedInitExpr>(Init))
return true;
return false;
}
-InitListChecker::InitListChecker(Sema &S, const InitializedEntity &Entity,
- InitListExpr *IL, QualType &T, bool VerifyOnly,
- bool TreatUnavailableAsInvalid,
- bool InOverloadResolution)
+InitListChecker::InitListChecker(
+ Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T,
+ bool VerifyOnly, bool TreatUnavailableAsInvalid, bool InOverloadResolution,
+ SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes)
: SemaRef(S), VerifyOnly(VerifyOnly),
TreatUnavailableAsInvalid(TreatUnavailableAsInvalid),
- InOverloadResolution(InOverloadResolution) {
+ InOverloadResolution(InOverloadResolution),
+ AggrDeductionCandidateParamTypes(AggrDeductionCandidateParamTypes) {
if (!VerifyOnly || hasAnyDesignatedInits(IL)) {
FullyStructuredList =
createInitListExpr(T, IL->getSourceRange(), IL->getNumInits());
@@ -980,7 +995,7 @@ InitListChecker::InitListChecker(Sema &S, const InitializedEntity &Entity,
CheckExplicitInitList(Entity, IL, T, FullyStructuredList,
/*TopLevelObject=*/true);
- if (!hadError && FullyStructuredList) {
+ if (!hadError && !AggrDeductionCandidateParamTypes && FullyStructuredList) {
bool RequiresSecondPass = false;
FillInEmptyInitializations(Entity, FullyStructuredList, RequiresSecondPass,
/*OuterILE=*/nullptr, /*OuterIndex=*/0);
@@ -1016,6 +1031,14 @@ int InitListChecker::numStructUnionElements(QualType DeclType) {
return InitializableMembers - structDecl->hasFlexibleArrayMember();
}
+RecordDecl *InitListChecker::getRecordDecl(QualType DeclType) {
+ if (const auto *RT = DeclType->getAs<RecordType>())
+ return RT->getDecl();
+ if (const auto *Inject = DeclType->getAs<InjectedClassNameType>())
+ return Inject->getDecl();
+ return nullptr;
+}
+
/// Determine whether Entity is an entity for which it is idiomatic to elide
/// the braces in aggregate initialization.
static bool isIdiomaticBraceElisionEntity(const InitializedEntity &Entity) {
@@ -1310,15 +1333,18 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity,
} else if (DeclType->isVectorType()) {
CheckVectorType(Entity, IList, DeclType, Index,
StructuredList, StructuredIndex);
- } else if (DeclType->isRecordType()) {
- assert(DeclType->isAggregateType() &&
- "non-aggregate records should be handed in CheckSubElementType");
- RecordDecl *RD = DeclType->castAs<RecordType>()->getDecl();
+ } else if (const RecordDecl *RD = getRecordDecl(DeclType)) {
auto Bases =
- CXXRecordDecl::base_class_range(CXXRecordDecl::base_class_iterator(),
- CXXRecordDecl::base_class_iterator());
- if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
- Bases = CXXRD->bases();
+ CXXRecordDecl::base_class_const_range(CXXRecordDecl::base_class_const_iterator(),
+ CXXRecordDecl::base_class_const_iterator());
+ if (DeclType->isRecordType()) {
+ assert(DeclType->isAggregateType() &&
+ "non-aggregate records should be handed in CheckSubElementType");
+ if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+ Bases = CXXRD->bases();
+ } else {
+ Bases = cast<CXXRecordDecl>(RD)->bases();
+ }
CheckStructUnionTypes(Entity, IList, DeclType, Bases, RD->field_begin(),
SubobjectIsDesignatorContext, Index, StructuredList,
StructuredIndex, TopLevelObject);
@@ -1348,6 +1374,13 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity,
// Checks for scalar type are sufficient for these types too.
CheckScalarType(Entity, IList, DeclType, Index, StructuredList,
StructuredIndex);
+ } else if (DeclType->isDependentType()) {
+ // C++ [over.match.class.deduct]p1.5:
+ // brace elision is not considered for any aggregate element that has a
+ // dependent non-array type or an array type with a value-dependent bound
+ ++Index;
+ assert(AggrDeductionCandidateParamTypes);
+ AggrDeductionCandidateParamTypes->push_back(DeclType);
} else {
if (!VerifyOnly)
SemaRef.Diag(IList->getBeginLoc(), diag::err_illegal_initializer_type)
@@ -1405,31 +1438,46 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity,
? InitializedEntity::InitializeTemporary(ElemType)
: Entity;
- InitializationSequence Seq(SemaRef, TmpEntity, Kind, expr,
- /*TopLevelOfInitList*/ true);
+ if (TmpEntity.getType()->isDependentType()) {
+ // C++ [over.match.class.deduct]p1.5:
+ // brace elision is not considered for any aggregate element that has a
+ // dependent non-array type or an array type with a value-dependent
+ // bound
+ assert(AggrDeductionCandidateParamTypes);
+ if (!isa_and_nonnull<ConstantArrayType>(
+ SemaRef.Context.getAsArrayType(ElemType))) {
+ ++Index;
+ AggrDeductionCandidateParamTypes->push_back(ElemType);
+ return;
+ }
+ } else {
+ InitializationSequence Seq(SemaRef, TmpEntity, Kind, expr,
+ /*TopLevelOfInitList*/ true);
+ // C++14 [dcl.init.aggr]p13:
+ // If the assignment-expression can initialize a member, the member is
+ // initialized. Otherwise [...] brace elision is assumed
+ //
+ // Brace elision is never performed if the element is not an
+ // assignment-expression.
+ if (Seq || isa<InitListExpr>(expr)) {
+ if (!VerifyOnly) {
+ ExprResult Result = Seq.Perform(SemaRef, TmpEntity, Kind, expr);
+ if (Result.isInvalid())
+ hadError = true;
- // C++14 [dcl.init.aggr]p13:
- // If the assignment-expression can initialize a member, the member is
- // initialized. Otherwise [...] brace elision is assumed
- //
- // Brace elision is never performed if the element is not an
- // assignment-expression.
- if (Seq || isa<InitListExpr>(expr)) {
- if (!VerifyOnly) {
- ExprResult Result = Seq.Perform(SemaRef, TmpEntity, Kind, expr);
- if (Result.isInvalid())
+ UpdateStructuredListElement(StructuredList, StructuredIndex,
+ Result.getAs<Expr>());
+ } else if (!Seq) {
hadError = true;
-
- UpdateStructuredListElement(StructuredList, StructuredIndex,
- Result.getAs<Expr>());
- } else if (!Seq) {
- hadError = true;
- } else if (StructuredList) {
- UpdateStructuredListElement(StructuredList, StructuredIndex,
- getDummyInit());
+ } else if (StructuredList) {
+ UpdateStructuredListElement(StructuredList, StructuredIndex,
+ getDummyInit());
+ }
+ ++Index;
+ if (AggrDeductionCandidateParamTypes)
+ AggrDeductionCandidateParamTypes->push_back(ElemType);
+ return;
}
- ++Index;
- return;
}
// Fall through for subaggregate initialization
@@ -1645,6 +1693,8 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity,
}
UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr);
++Index;
+ if (AggrDeductionCandidateParamTypes)
+ AggrDeductionCandidateParamTypes->push_back(DeclType);
}
void InitListChecker::CheckReferenceType(const InitializedEntity &Entity,
@@ -1700,6 +1750,8 @@ void InitListChecker::CheckReferenceType(const InitializedEntity &Entity,
UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
++Index;
+ if (AggrDeductionCandidateParamTypes)
+ AggrDeductionCandidateParamTypes->push_back(DeclType);
}
void InitListChecker::CheckVectorType(const InitializedEntity &Entity,
@@ -1751,6 +1803,8 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity,
}
UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr);
++Index;
+ if (AggrDeductionCandidateParamTypes)
+ AggrDeductionCandidateParamTypes->push_back(elementType);
return;
}
@@ -1912,6 +1966,8 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity,
StructuredList->resizeInits(SemaRef.Context, StructuredIndex);
}
++Index;
+ if (AggrDeductionCandidateParamTypes)
+ AggrDeductionCandidateParamTypes->push_back(DeclType);
return;
}
}
@@ -2067,24 +2123,22 @@ bool InitListChecker::CheckFlexibleArrayInit(const InitializedEntity &Entity,
void InitListChecker::CheckStructUnionTypes(
const InitializedEntity &Entity, InitListExpr *IList, QualType DeclType,
- CXXRecordDecl::base_class_range Bases, RecordDecl::field_iterator Field,
+ CXXRecordDecl::base_class_const_range Bases, RecordDecl::field_iterator Field,
bool SubobjectIsDesignatorContext, unsigned &Index,
InitListExpr *StructuredList, unsigned &StructuredIndex,
bool TopLevelObject) {
- RecordDecl *structDecl = DeclType->castAs<RecordType>()->getDecl();
+ const RecordDecl *RD = getRecordDecl(DeclType);
// If the record is invalid, some of it's members are invalid. To avoid
// confusion, we forgo checking the initializer for the entire record.
- if (structDecl->isInvalidDecl()) {
+ if (RD->isInvalidDecl()) {
// Assume it was supposed to consume a single initializer.
++Index;
hadError = true;
return;
}
- if (DeclType->isUnionType() && IList->getNumInits() == 0) {
- RecordDecl *RD = DeclType->castAs<RecordType>()->getDecl();
-
+ if (RD->isUnion() && IList->getNumInits() == 0) {
if (!VerifyOnly)
for (FieldDecl *FD : RD->fields()) {
QualType ET = SemaRef.Context.getBaseElementType(FD->getType());
@@ -2128,7 +2182,8 @@ void InitListChecker::CheckStructUnionTypes(
bool InitializedSomething = false;
// If we have any base classes, they are initialized prior to the fields.
- for (auto &Base : Bases) {
+ for (auto I = Bases.begin(), E = Bases.end(); I != E; ++I) {
+ auto &Base = *I;
Expr *Init = Index < IList->getNumInits() ? IList->getInit(Index) : nullptr;
// Designated inits always initialize fields, so if we see one, all
@@ -2136,6 +2191,34 @@ void InitListChecker::CheckStructUnionTypes(
if (Init && isa<DesignatedInitExpr>(Init))
Init = nullptr;
+ // C++ [over.match.class.deduct]p1.6:
+ // each non-trailing aggregate element that is a pack expansion is assumed
+ // to correspond to no elements of the initializer list, and (1.7) a
+ // trailing aggregate element that is a pack expansion is assumed to
+ // correspond to all remaining elements of the initializer list (if any).
+
+ // C++ [over.match.class.deduct]p1.9:
+ // ... except that additional parameter packs of the form P_j... are
+ // inserted into the parameter list in their original aggregate element
+ // position corresponding to each non-trailing aggregate element of
+ // type P_j that was skipped because it was a parameter pack, and the
+ // trailing sequence of parameters corresponding to a trailing
+ // aggregate element that is a pack expansion (if any) is replaced
+ // by a single parameter of the form T_n....
+ if (AggrDeductionCandidateParamTypes && Base.isPackExpansion()) {
+ AggrDeductionCandidateParamTypes->push_back(
+ SemaRef.Context.getPackExpansionType(Base.getType(), std::nullopt));
+
+ // Trailing pack expansion
+ if (I + 1 == E && RD->field_empty()) {
+ if (Index < IList->getNumInits())
+ Index = IList->getNumInits();
+ return;
+ }
+
+ continue;
+ }
+
SourceLocation InitLoc = Init ? Init->getBeginLoc() : IList->getEndLoc();
InitializedEntity BaseEntity = InitializedEntity::InitializeBase(
SemaRef.Context, &Base, false, &Entity);
@@ -2158,7 +2241,6 @@ void InitListChecker::CheckStructUnionTypes(
// anything except look at designated initializers; That's okay,
// because an error should get printed out elsewhere. It might be
// worthwhile to skip over the rest of the initializer, though.
- RecordDecl *RD = DeclType->castAs<RecordType>()->getDecl();
RecordDecl::field_iterator FieldEnd = RD->field_end();
size_t NumRecordDecls = llvm::count_if(RD->decls(), [&](const Decl *D) {
return isa<FieldDecl>(D) || isa<RecordDecl>(D);
@@ -2242,7 +2324,7 @@ void InitListChecker::CheckStructUnionTypes(
}
// We've already initialized a member of a union. We're done.
- if (InitializedSomething && DeclType->isUnionType())
+ if (InitializedSomething && RD->isUnion())
break;
// If we've hit the flexible array member at the end, we're done.
@@ -2283,7 +2365,7 @@ void InitListChecker::CheckStructUnionTypes(
StructuredList, StructuredIndex);
InitializedSomething = true;
- if (DeclType->isUnionType() && StructuredList) {
+ if (RD->isUnion() && StructuredList) {
// Initialize the first field within the union.
StructuredList->setInitializedFieldInUnion(*Field);
}
@@ -2294,7 +2376,7 @@ void InitListChecker::CheckStructUnionTypes(
// Emit warnings for missing struct field initializers.
if (!VerifyOnly && InitializedSomething && CheckForMissingFields &&
Field != FieldEnd && !Field->getType()->isIncompleteArrayType() &&
- !DeclType->isUnionType()) {
+ !RD->isUnion()) {
// It is possible we have one or more unnamed bitfields remaining.
// Find first (if any) named field and emit warning.
for (RecordDecl::field_iterator it = Field, end = RD->field_end();
@@ -2309,7 +2391,7 @@ void InitListChecker::CheckStructUnionTypes(
// Check that any remaining fields can be value-initialized if we're not
// building a structured list. (If we are, we'll check this later.)
- if (!StructuredList && Field != FieldEnd && !DeclType->isUnionType() &&
+ if (!StructuredList && Field != FieldEnd && !RD->isUnion() &&
!Field->getType()->isIncompleteArrayType()) {
for (; Field != FieldEnd && !hadError; ++Field) {
if (!Field->isUnnamedBitfield() && !Field->hasInClassInitializer())
@@ -2348,7 +2430,8 @@ void InitListChecker::CheckStructUnionTypes(
InitializedEntity MemberEntity =
InitializedEntity::InitializeMember(*Field, &Entity);
- if (isa<InitListExpr>(IList->getInit(Index)))
+ if (isa<InitListExpr>(IList->getInit(Index)) ||
+ AggrDeductionCandidateParamTypes)
CheckSubElementType(MemberEntity, IList, Field->getType(), Index,
StructuredList, StructuredIndex);
else
@@ -2406,7 +2489,7 @@ namespace {
// the given struct or union.
class FieldInitializerValidatorCCC final : public CorrectionCandidateCallback {
public:
- explicit FieldInitializerValidatorCCC(RecordDecl *RD)
+ explicit FieldInitializerValidatorCCC(const RecordDecl *RD)
: Record(RD) {}
bool ValidateCandidate(const TypoCorrection &candidate) override {
@@ -2419,7 +2502,7 @@ class FieldInitializerValidatorCCC final : public CorrectionCandidateCallback {
}
private:
- RecordDecl *Record;
+ const RecordDecl *Record;
};
} // end anonymous namespace
@@ -2495,6 +2578,8 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
Result.get());
}
++Index;
+ if (AggrDeductionCandidateParamTypes)
+ AggrDeductionCandidateParamTypes->push_back(CurrentObjectType);
return !Seq;
}
@@ -2588,8 +2673,8 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
// then the current object (defined below) shall have
// structure or union type and the identifier shall be the
// name of a member of that type.
- const RecordType *RT = CurrentObjectType->getAs<RecordType>();
- if (!RT) {
+ RecordDecl *RD = getRecordDecl(CurrentObjectType);
+ if (!RD) {
SourceLocation Loc = D->getDotLoc();
if (Loc.isInvalid())
Loc = D->getFieldLoc();
@@ -2603,7 +2688,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
FieldDecl *KnownField = D->getFieldDecl();
if (!KnownField) {
const IdentifierInfo *FieldName = D->getFieldName();
- DeclContext::lookup_result Lookup = RT->getDecl()->lookup(FieldName);
+ DeclContext::lookup_result Lookup = RD->lookup(FieldName);
for (NamedDecl *ND : Lookup) {
if (auto *FD = dyn_cast<FieldDecl>(ND)) {
KnownField = FD;
@@ -2637,11 +2722,11 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
// Name lookup didn't find anything.
// Determine whether this was a typo for another field name.
- FieldInitializerValidatorCCC CCC(RT->getDecl());
+ FieldInitializerValidatorCCC CCC(RD);
if (TypoCorrection Corrected = SemaRef.CorrectTypo(
DeclarationNameInfo(FieldName, D->getFieldLoc()),
Sema::LookupMemberName, /*Scope=*/nullptr, /*SS=*/nullptr, CCC,
- Sema::CTK_ErrorRecovery, RT->getDecl())) {
+ Sema::CTK_ErrorRecovery, RD)) {
SemaRef.diagnoseTypo(
Corrected,
SemaRef.PDiag(diag::err_field_designator_unknown_suggest)
@@ -2666,12 +2751,12 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
}
unsigned NumBases = 0;
- if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RT->getDecl()))
+ if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
NumBases = CXXRD->getNumBases();
unsigned FieldIndex = NumBases;
- for (auto *FI : RT->getDecl()->fields()) {
+ for (auto *FI : RD->fields()) {
if (FI->isUnnamedBitfield())
continue;
if (declaresSameEntity(KnownField, FI)) {
@@ -2686,7 +2771,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
// All of the fields of a union are located at the same place in
// the initializer list.
- if (RT->getDecl()->isUnion()) {
+ if (RD->isUnion()) {
FieldIndex = 0;
if (StructuredList) {
FieldDecl *CurrentField = StructuredList->getInitializedFieldInUnion();
@@ -2741,15 +2826,14 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
// cases where a designator takes us backwards too.
if (IsFirstDesignator && !VerifyOnly && SemaRef.getLangOpts().CPlusPlus &&
NextField &&
- (*NextField == RT->getDecl()->field_end() ||
+ (*NextField == RD->field_end() ||
(*NextField)->getFieldIndex() > Field->getFieldIndex() + 1)) {
// Find the field that we just initialized.
FieldDecl *PrevField = nullptr;
- for (auto FI = RT->getDecl()->field_begin();
- FI != RT->getDecl()->field_end(); ++FI) {
+ for (auto FI = RD->field_begin(); FI != RD->field_end(); ++FI) {
if (FI->isUnnamedBitfield())
continue;
- if (*NextField != RT->getDecl()->field_end() &&
+ if (*NextField != RD->field_end() &&
declaresSameEntity(*FI, **NextField))
break;
PrevField = *FI;
@@ -2874,7 +2958,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
return false;
// We've already initialized something in the union; we're done.
- if (RT->getDecl()->isUnion())
+ if (RD->isUnion())
return hadError;
// Check the remaining fields within this class/struct/union subobject.
@@ -3182,6 +3266,8 @@ InitListChecker::createInitListExpr(QualType CurrentObjectType,
NumElements = VType->getNumElements();
} else if (CurrentObjectType->isRecordType()) {
NumElements = numStructUnionElements(CurrentObjectType);
+ } else if (CurrentObjectType->isDependentType()) {
+ NumElements = 1;
}
Result->reserveInits(SemaRef.Context, NumElements);
@@ -10459,7 +10545,7 @@ static bool isOrIsDerivedFromSpecializationOf(CXXRecordDecl *RD,
QualType Sema::DeduceTemplateSpecializationFromInitializer(
TypeSourceInfo *TSInfo, const InitializedEntity &Entity,
- const InitializationKind &Kind, MultiExprArg Inits) {
+ const InitializationKind &Kind, MultiExprArg Inits, ParenListExpr *PL) {
auto *DeducedTST = dyn_cast<DeducedTemplateSpecializationType>(
TSInfo->getType()->getContainedDeducedType());
assert(DeducedTST && "not a deduced template specialization type");
@@ -10529,13 +10615,136 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
OverloadCandidateSet::CSK_Normal);
OverloadCandidateSet::iterator Best;
- bool HasAnyDeductionGuide = false;
bool AllowExplicit = !Kind.isCopyInit() || ListInit;
- auto tryToResolveOverload =
+ // Return true is the candidate is added successfully, false otherwise.
+ auto addDeductionCandidate = [&](FunctionTemplateDecl *TD,
+ CXXDeductionGuideDecl *GD,
+ DeclAccessPair FoundDecl,
+ bool OnlyListConstructors,
+ bool AllowAggregateDeductionCandidate) {
+ // C++ [over.match.ctor]p1: (non-list copy-initialization from non-class)
+ // For copy-initialization, the candidate functions are all the
+ // converting constructors (12.3.1) of that class.
+ // C++ [over.match.copy]p1: (non-list copy-initialization from class)
+ // The converting constructors of T are candidate functions.
+ if (!AllowExplicit) {
+ // Overload resolution checks whether the deduction guide is declared
+ // explicit for us.
+
+ // When looking for a converting constructor, deduction guides that
+ // could never be called with one argument are not interesting to
+ // check or note.
+ if (GD->getMinRequiredArguments() > 1 ||
+ (GD->getNumParams() == 0 && !GD->isVariadic()))
+ return;
+ }
+
+ // C++ [over.match.list]p1.1: (first phase list initialization)
+ // Initially, the candidate functions are the initializer-list
+ // constructors of the class T
+ if (OnlyListConstructors && !isInitListConstructor(GD))
+ return;
+
+ if (!AllowAggregateDeductionCandidate &&
+ GD->getDeductionCandidateKind() == DeductionCandidate::Aggregate)
+ return;
+
+ // C++ [over.match.list]p1.2: (second phase list initialization)
+ // the candidate functions are all the constructors of the class T
+ // C++ [over.match.ctor]p1: (all other cases)
+ // the candidate functions are all the constructors of the class of
+ // the object being initialized
+
+ // C++ [over.best.ics]p4:
+ // When [...] the constructor [...] is a candidate by
+ // - [over.match.copy] (in all cases)
+ // FIXME: The "second phase of [over.match.list] case can also
+ // theoretically happen here, but it's not clear whether we can
+ // ever have a parameter of the right type.
+ bool SuppressUserConversions = Kind.isCopyInit();
+
+ if (TD) {
+ SmallVector<Expr *, 8> TmpInits;
+ for (Expr *E : Inits)
+ if (auto *DI = dyn_cast<DesignatedInitExpr>(E))
+ TmpInits.push_back(DI->getInit());
+ else
+ TmpInits.push_back(E);
+ AddTemplateOverloadCandidate(
+ TD, FoundDecl, /*ExplicitArgs=*/nullptr, TmpInits, Candidates,
+ SuppressUserConversions,
+ /*PartialOverloading=*/false, AllowExplicit, ADLCallKind::NotADL,
+ /*PO=*/{}, /*AggregateCandidateDeduction=*/true);
+ } else {
+ AddOverloadCandidate(GD, FoundDecl, Inits, Candidates,
+ SuppressUserConversions,
+ /*PartialOverloading=*/false, AllowExplicit);
+ }
+ };
+
+ bool FoundDeductionGuide = false;
+
+ auto TryToResolveOverload =
[&](bool OnlyListConstructors) -> OverloadingResult {
Candidates.clear(OverloadCandidateSet::CSK_Normal);
- HasAnyDeductionGuide = false;
+ bool HasAnyDeductionGuide = false;
+
+ auto SynthesizeAggrGuide = [&](InitListExpr *ListInit) {
+ auto *RD = cast<CXXRecordDecl>(Template->getTemplatedDecl());
+ if (!(RD->getDefinition() && RD->isAggregate()))
+ return;
+ QualType Ty = Context.getRecordType(RD);
+ SmallVector<QualType, 8> ElementTypes;
+
+ InitListChecker CheckInitList(*this, Entity, ListInit, Ty, ElementTypes);
+ if (!CheckInitList.HadError()) {
+ // C++ [over.match.class.deduct]p1.8:
+ // if e_i is of array type and x_i is a braced-init-list, T_i is an
+ // rvalue reference to the declared type of e_i and
+ // C++ [over.match.class.deduct]p1.9:
+ // if e_i is of array type and x_i is a bstring-literal, T_i is an
+ // lvalue reference to the const-qualified declared type of e_i and
+ // C++ [over.match.class.deduct]p1.10:
+ // otherwise, T_i is the declared type of e_i
+ for (int I = 0, E = ListInit->getNumInits();
+ I < E && !isa<PackExpansionType>(ElementTypes[I]); ++I)
+ if (ElementTypes[I]->isArrayType()) {
+ if (isa<InitListExpr>(ListInit->getInit(I)))
+ ElementTypes[I] = Context.getRValueReferenceType(ElementTypes[I]);
+ else if (isa<StringLiteral>(
+ ListInit->getInit(I)->IgnoreParenImpCasts()))
+ ElementTypes[I] = Context.getLValueReferenceType(ElementTypes[I]);
+ }
+
+ llvm::FoldingSetNodeID ID;
+ ID.AddPointer(Template);
+ for (auto &T : ElementTypes)
+ T.getCanonicalType().Profile(ID);
+ unsigned Hash = ID.ComputeHash();
+ if (AggregateDeductionCandidates.count(Hash) == 0) {
+ if (FunctionTemplateDecl *TD =
+ DeclareImplicitDeductionGuideFromInitList(
+ Template, ElementTypes,
+ TSInfo->getTypeLoc().getEndLoc())) {
+ auto *GD = cast<CXXDeductionGuideDecl>(TD->getTemplatedDecl());
+ GD->setDeductionCandidateKind(DeductionCandidate::Aggregate);
+ AggregateDeductionCandidates[Hash] = GD;
+ addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public),
+ OnlyListConstructors,
+ /*AllowAggregateDeductionCandidate=*/true);
+ }
+ } else {
+ CXXDeductionGuideDecl *GD = AggregateDeductionCandidates[Hash];
+ FunctionTemplateDecl *TD = GD->getDescribedFunctionTemplate();
+ assert(TD && "aggregate deduction candidate is function template");
+ addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public),
+ OnlyListConstructors,
+ /*AllowAggregateDeductionCandidate=*/true);
+ }
+ HasAnyDeductionGuide = true;
+ }
+ };
for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) {
NamedDecl *D = (*I)->getUnderlyingDecl();
@@ -10543,7 +10752,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
continue;
auto *TD = dyn_cast<FunctionTemplateDecl>(D);
- auto *GD = dyn_cast_or_null<CXXDeductionGuideDecl>(
+ auto *GD = dyn_cast_if_present<CXXDeductionGuideDecl>(
TD ? TD->getTemplatedDecl() : dyn_cast<FunctionDecl>(D));
if (!GD)
continue;
@@ -10551,53 +10760,30 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
if (!GD->isImplicit())
HasAnyDeductionGuide = true;
- // C++ [over.match.ctor]p1: (non-list copy-initialization from non-class)
- // For copy-initialization, the candidate functions are all the
- // converting constructors (12.3.1) of that class.
- // C++ [over.match.copy]p1: (non-list copy-initialization from class)
- // The converting constructors of T are candidate functions.
- if (!AllowExplicit) {
- // Overload resolution checks whether the deduction guide is declared
- // explicit for us.
-
- // When looking for a converting constructor, deduction guides that
- // could never be called with one argument are not interesting to
- // check or note.
- if (GD->getMinRequiredArguments() > 1 ||
- (GD->getNumParams() == 0 && !GD->isVariadic()))
- continue;
+ addDeductionCandidate(TD, GD, I.getPair(), OnlyListConstructors,
+ /*AllowAggregateDeductionCandidate=*/false);
+ }
+
+ // C++ [over.match.class.deduct]p1.4:
+ // if C is defined and its definition satisfies the conditions for an
+ // aggregate class ([dcl.init.aggr]) with the assumption that any
+ // dependent base class has no virtual functions and no virtual base
+ // classes, and the initializer is a non-empty braced-init-list or
+ // parenthesized expression-list, and there are no deduction-guides for
+ // C, the set contains an additional function template, called the
+ // aggregate deduction candidate, defined as follows.
+ if (!HasAnyDeductionGuide) {
+ if (ListInit && ListInit->getNumInits()) {
+ SynthesizeAggrGuide(ListInit);
+ } else if (PL && PL->getNumExprs()) {
+ InitListExpr TempListInit(getASTContext(), PL->getLParenLoc(),
+ PL->exprs(), PL->getRParenLoc());
+ SynthesizeAggrGuide(&TempListInit);
}
+ }
- // C++ [over.match.list]p1.1: (first phase list initialization)
- // Initially, the candidate functions are the initializer-list
- // constructors of the class T
- if (OnlyListConstructors && !isInitListConstructor(GD))
- continue;
+ FoundDeductionGuide = FoundDeductionGuide || HasAnyDeductionGuide;
- // C++ [over.match.list]p1.2: (second phase list initialization)
- // the candidate functions are all the constructors of the class T
- // C++ [over.match.ctor]p1: (all other cases)
- // the candidate functions are all the constructors of the class of
- // the object being initialized
-
- // C++ [over.best.ics]p4:
- // When [...] the constructor [...] is a candidate by
- // - [over.match.copy] (in all cases)
- // FIXME: The "second phase of [over.match.list] case can also
- // theoretically happen here, but it's not clear whether we can
- // ever have a parameter of the right type.
- bool SuppressUserConversions = Kind.isCopyInit();
-
- if (TD)
- AddTemplateOverloadCandidate(TD, I.getPair(), /*ExplicitArgs*/ nullptr,
- Inits, Candidates, SuppressUserConversions,
- /*PartialOverloading*/ false,
- AllowExplicit);
- else
- AddOverloadCandidate(GD, I.getPair(), Inits, Candidates,
- SuppressUserConversions,
- /*PartialOverloading*/ false, AllowExplicit);
- }
return Candidates.BestViableFunction(*this, Kind.getLocation(), Best);
};
@@ -10633,7 +10819,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
}
if (TryListConstructors)
- Result = tryToResolveOverload(/*OnlyListConstructor*/true);
+ Result = TryToResolveOverload(/*OnlyListConstructor*/true);
// Then unwrap the initializer list and try again considering all
// constructors.
Inits = MultiExprArg(ListInit->getInits(), ListInit->getNumInits());
@@ -10642,7 +10828,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// If list-initialization fails, or if we're doing any other kind of
// initialization, we (eventually) consider constructors.
if (Result == OR_No_Viable_Function)
- Result = tryToResolveOverload(/*OnlyListConstructor*/false);
+ Result = TryToResolveOverload(/*OnlyListConstructor*/false);
switch (Result) {
case OR_Ambiguous:
@@ -10712,7 +10898,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer(
// Warn if CTAD was used on a type that does not have any user-defined
// deduction guides.
- if (!HasAnyDeductionGuide) {
+ if (!FoundDeductionGuide) {
Diag(TSInfo->getTypeLoc().getBeginLoc(),
diag::warn_ctad_maybe_unsupported)
<< TemplateName;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index febcbad1f3727a..d45257ec7a4c7b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6468,7 +6468,7 @@ void Sema::AddOverloadCandidate(
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
bool PartialOverloading, bool AllowExplicit, bool AllowExplicitConversions,
ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions,
- OverloadCandidateParamOrder PO) {
+ OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) {
const FunctionProtoType *Proto
= dyn_cast<FunctionProtoType>(Function->getType()->getAs<FunctionType>());
assert(Proto && "Functions without a prototype cannot be overloaded");
@@ -6635,7 +6635,8 @@ void Sema::AddOverloadCandidate(
// parameter list is truncated on the right, so that there are
// exactly m parameters.
unsigned MinRequiredArgs = Function->getMinRequiredArguments();
- if (Args.size() < MinRequiredArgs && !PartialOverloading) {
+ if (!AggregateCandidateDeduction && Args.size() < MinRequiredArgs &&
+ !PartialOverloading) {
// Not enough arguments.
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_too_few_arguments;
@@ -7261,7 +7262,8 @@ void Sema::AddMethodTemplateCandidate(
ConversionSequenceList Conversions;
if (TemplateDeductionResult Result = DeduceTemplateArguments(
MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info,
- PartialOverloading, [&](ArrayRef<QualType> ParamTypes) {
+ PartialOverloading, /*AggregateDeductionCandidate=*/false,
+ [&](ArrayRef<QualType> ParamTypes) {
return CheckNonDependentConversions(
MethodTmpl, ParamTypes, Args, CandidateSet, Conversions,
SuppressUserConversions, ActingContext, ObjectType,
@@ -7314,7 +7316,7 @@ void Sema::AddTemplateOverloadCandidate(
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
bool PartialOverloading, bool AllowExplicit, ADLCallKind IsADLCandidate,
- OverloadCandidateParamOrder PO) {
+ OverloadCandidateParamOrder PO, bool AggregateCandidateDeduction) {
if (!CandidateSet.isNewCandidate(FunctionTemplate, PO))
return;
@@ -7344,7 +7346,8 @@ void Sema::AddTemplateOverloadCandidate(
ConversionSequenceList Conversions;
if (TemplateDeductionResult Result = DeduceTemplateArguments(
FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info,
- PartialOverloading, [&](ArrayRef<QualType> ParamTypes) {
+ PartialOverloading, AggregateCandidateDeduction,
+ [&](ArrayRef<QualType> ParamTypes) {
return CheckNonDependentConversions(
FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions,
SuppressUserConversions, nullptr, QualType(), {}, PO);
@@ -7380,7 +7383,8 @@ void Sema::AddTemplateOverloadCandidate(
AddOverloadCandidate(
Specialization, FoundDecl, Args, CandidateSet, SuppressUserConversions,
PartialOverloading, AllowExplicit,
- /*AllowExplicitConversions*/ false, IsADLCandidate, Conversions, PO);
+ /*AllowExplicitConversions=*/false, IsADLCandidate, Conversions, PO,
+ Info.AggregateDeductionCandidateHasMismatchedArity);
}
/// Check that implicit conversion sequences can be formed for each argument
@@ -10147,7 +10151,7 @@ bool clang::isBetterOverloadCandidate(
return Guide2->isImplicit();
// -- F1 is the copy deduction candidate(16.3.1.8) and F2 is not
- if (Guide1->isCopyDeductionCandidate())
+ if (Guide1->getDeductionCandidateKind() == DeductionCandidate::Copy)
return true;
}
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 063ddb418c4314..7ef99b4bfcaf54 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2570,12 +2570,47 @@ struct ConvertConstructorToDeductionGuideTransform {
};
}
+FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList(
+ TemplateDecl *Template, MutableArrayRef<QualType> ParamTypes,
+ SourceLocation Loc) {
+ if (CXXRecordDecl *DefRecord =
+ cast<CXXRecordDecl>(Template->getTemplatedDecl())->getDefinition()) {
+ if (TemplateDecl *DescribedTemplate =
+ DefRecord->getDescribedClassTemplate())
+ Template = DescribedTemplate;
+ }
+
+ DeclContext *DC = Template->getDeclContext();
+ if (DC->isDependentContext())
+ return nullptr;
+
+ ConvertConstructorToDeductionGuideTransform Transform(
+ *this, cast<ClassTemplateDecl>(Template));
+ if (!isCompleteType(Loc, Transform.DeducedType))
+ return nullptr;
+
+ // In case we were expanding a pack when we attempted to declare deduction
+ // guides, turn off pack expansion for everything we're about to do.
+ ArgumentPackSubstitutionIndexRAII SubstIndex(*this,
+ /*NewSubstitutionIndex=*/-1);
+ // Create a template instantiation record to track the "instantiation" of
+ // constructors into deduction guides.
+ InstantiatingTemplate BuildingDeductionGuides(
+ *this, Loc, Template,
+ Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{});
+ if (BuildingDeductionGuides.isInvalid())
+ return nullptr;
+
+ return cast<FunctionTemplateDecl>(
+ Transform.buildSimpleDeductionGuide(ParamTypes));
+}
+
void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
SourceLocation Loc) {
if (CXXRecordDecl *DefRecord =
cast<CXXRecordDecl>(Template->getTemplatedDecl())->getDefinition()) {
- TemplateDecl *DescribedTemplate = DefRecord->getDescribedClassTemplate();
- Template = DescribedTemplate ? DescribedTemplate : Template;
+ if (TemplateDecl *DescribedTemplate = DefRecord->getDescribedClassTemplate())
+ Template = DescribedTemplate;
}
DeclContext *DC = Template->getDeclContext();
@@ -2599,9 +2634,9 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
ArgumentPackSubstitutionIndexRAII SubstIndex(*this, -1);
// Create a template instantiation record to track the "instantiation" of
// constructors into deduction guides.
- // FIXME: Add a kind for this to give more meaningful diagnostics. But can
- // this substitution process actually fail?
- InstantiatingTemplate BuildingDeductionGuides(*this, Loc, Template);
+ InstantiatingTemplate BuildingDeductionGuides(
+ *this, Loc, Template,
+ Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{});
if (BuildingDeductionGuides.isInvalid())
return;
@@ -2656,7 +2691,7 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
cast<FunctionTemplateDecl>(
Transform.buildSimpleDeductionGuide(Transform.DeducedType))
->getTemplatedDecl())
- ->setIsCopyDeductionCandidate();
+ ->setDeductionCandidateKind(DeductionCandidate::Copy);
}
/// Diagnose the presence of a default template argument on a
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 6328149f5fdcf7..425017aa213580 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -212,7 +212,8 @@ static bool isSameDeclaration(Decl *X, Decl *Y) {
static DeducedTemplateArgument
checkDeducedTemplateArguments(ASTContext &Context,
const DeducedTemplateArgument &X,
- const DeducedTemplateArgument &Y) {
+ const DeducedTemplateArgument &Y,
+ bool AggregateCandidateDeduction = false) {
// We have no deduction for one or both of the arguments; they're compatible.
if (X.isNull())
return Y;
@@ -350,20 +351,24 @@ checkDeducedTemplateArguments(ASTContext &Context,
case TemplateArgument::Pack: {
if (Y.getKind() != TemplateArgument::Pack ||
- X.pack_size() != Y.pack_size())
+ (!AggregateCandidateDeduction && X.pack_size() != Y.pack_size()))
return DeducedTemplateArgument();
llvm::SmallVector<TemplateArgument, 8> NewPack;
- for (TemplateArgument::pack_iterator XA = X.pack_begin(),
- XAEnd = X.pack_end(),
- YA = Y.pack_begin();
+ for (TemplateArgument::pack_iterator
+ XA = X.pack_begin(),
+ XAEnd = X.pack_end(), YA = Y.pack_begin(), YAEnd = Y.pack_end();
XA != XAEnd; ++XA, ++YA) {
- TemplateArgument Merged = checkDeducedTemplateArguments(
- Context, DeducedTemplateArgument(*XA, X.wasDeducedFromArrayBound()),
- DeducedTemplateArgument(*YA, Y.wasDeducedFromArrayBound()));
- if (Merged.isNull() && !(XA->isNull() && YA->isNull()))
- return DeducedTemplateArgument();
- NewPack.push_back(Merged);
+ if (YA != YAEnd) {
+ TemplateArgument Merged = checkDeducedTemplateArguments(
+ Context, DeducedTemplateArgument(*XA, X.wasDeducedFromArrayBound()),
+ DeducedTemplateArgument(*YA, Y.wasDeducedFromArrayBound()));
+ if (Merged.isNull() && !(XA->isNull() && YA->isNull()))
+ return DeducedTemplateArgument();
+ NewPack.push_back(Merged);
+ } else {
+ NewPack.push_back(*XA);
+ }
}
return DeducedTemplateArgument(
@@ -694,8 +699,10 @@ class PackDeductionScope {
/// Prepare to deduce the packs named within Pattern.
PackDeductionScope(Sema &S, TemplateParameterList *TemplateParams,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
- TemplateDeductionInfo &Info, TemplateArgument Pattern)
- : S(S), TemplateParams(TemplateParams), Deduced(Deduced), Info(Info) {
+ TemplateDeductionInfo &Info, TemplateArgument Pattern,
+ bool DeducePackIfNotAlreadyDeduced = false)
+ : S(S), TemplateParams(TemplateParams), Deduced(Deduced), Info(Info),
+ DeducePackIfNotAlreadyDeduced(DeducePackIfNotAlreadyDeduced){
unsigned NumNamedPacks = addPacks(Pattern);
finishConstruction(NumNamedPacks);
}
@@ -939,8 +946,13 @@ class PackDeductionScope {
// Check the new pack matches any previous value.
DeducedTemplateArgument OldPack = *Loc;
- DeducedTemplateArgument Result =
- checkDeducedTemplateArguments(S.Context, OldPack, NewPack);
+ DeducedTemplateArgument Result = checkDeducedTemplateArguments(
+ S.Context, OldPack, NewPack, DeducePackIfNotAlreadyDeduced);
+
+ Info.AggregateDeductionCandidateHasMismatchedArity =
+ OldPack.getKind() == TemplateArgument::Pack &&
+ NewPack.getKind() == TemplateArgument::Pack &&
+ OldPack.pack_size() != NewPack.pack_size() && !Result.isNull();
// If we deferred a deduction of this pack, check that one now too.
if (!Result.isNull() && !Pack.DeferredDeduction.isNull()) {
@@ -980,6 +992,7 @@ class PackDeductionScope {
TemplateDeductionInfo &Info;
unsigned PackElements = 0;
bool IsPartiallyExpanded = false;
+ bool DeducePackIfNotAlreadyDeduced = false;
/// The number of expansions, if we have a fully-expanded pack in this scope.
std::optional<unsigned> FixedNumExpansions;
@@ -4090,7 +4103,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
FunctionTemplateDecl *FunctionTemplate,
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
FunctionDecl *&Specialization, TemplateDeductionInfo &Info,
- bool PartialOverloading,
+ bool PartialOverloading, bool AggregateDeductionCandidate,
llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent) {
if (FunctionTemplate->isInvalidDecl())
return TDK_Invalid;
@@ -4177,9 +4190,12 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
continue;
}
+ bool IsTrailingPack = ParamIdx + 1 == NumParamTypes;
+
QualType ParamPattern = ParamExpansion->getPattern();
PackDeductionScope PackScope(*this, TemplateParams, Deduced, Info,
- ParamPattern);
+ ParamPattern,
+ AggregateDeductionCandidate && IsTrailingPack);
// C++0x [temp.deduct.call]p1:
// For a function parameter pack that occurs at the end of the
@@ -4197,7 +4213,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
// the length of the explicitly-specified pack if it's expanded by the
// parameter pack and 0 otherwise, and we treat each deduction as a
// non-deduced context.
- if (ParamIdx + 1 == NumParamTypes || PackScope.hasFixedArity()) {
+ if (IsTrailingPack || PackScope.hasFixedArity()) {
for (; ArgIdx < Args.size() && PackScope.hasNextElement();
PackScope.nextPackElement(), ++ArgIdx) {
ParamTypesForArgChecking.push_back(ParamPattern);
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 511d42f43007c6..f7e81d509c1cbc 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -404,6 +404,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
case MarkingClassDllexported:
case BuildingBuiltinDumpStructCall:
case LambdaExpressionSubstitution:
+ case BuildingDeductionGuides:
return false;
// This function should never be called when Kind's value is Memoization.
@@ -620,6 +621,13 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
SemaRef, CodeSynthesisContext::ParameterMappingSubstitution,
PointOfInstantiation, InstantiationRange, Template) {}
+Sema::InstantiatingTemplate::InstantiatingTemplate(
+ Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Entity,
+ BuildingDeductionGuidesTag, SourceRange InstantiationRange)
+ : InstantiatingTemplate(
+ SemaRef, CodeSynthesisContext::BuildingDeductionGuides,
+ PointOfInstantiation, InstantiationRange, Entity) {}
+
void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {
Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext;
@@ -1051,6 +1059,8 @@ void Sema::PrintInstantiationStack() {
diag::note_parameter_mapping_substitution_here)
<< Active->InstantiationRange;
break;
+ case CodeSynthesisContext::BuildingDeductionGuides:
+ llvm_unreachable("unexpected deduction guide in instantiation stack");
}
}
}
@@ -1122,6 +1132,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
case CodeSynthesisContext::InitializingStructuredBinding:
case CodeSynthesisContext::MarkingClassDllexported:
case CodeSynthesisContext::BuildingBuiltinDumpStructCall:
+ case CodeSynthesisContext::BuildingDeductionGuides:
// This happens in a context unrelated to template instantiation, so
// there is no SFINAE.
return std::nullopt;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index b7879e5a7f8548..492fe8ba1b143c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2129,9 +2129,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
Function = CXXDeductionGuideDecl::Create(
SemaRef.Context, DC, D->getInnerLocStart(),
InstantiatedExplicitSpecifier, NameInfo, T, TInfo,
- D->getSourceRange().getEnd());
- if (DGuide->isCopyDeductionCandidate())
- cast<CXXDeductionGuideDecl>(Function)->setIsCopyDeductionCandidate();
+ D->getSourceRange().getEnd(), /*Ctor=*/nullptr,
+ DGuide->getDeductionCandidateKind());
Function->setAccess(D->getAccess());
} else {
Function = FunctionDecl::Create(
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 2f052a5817a6f7..fe752eaa0f9192 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2219,7 +2219,8 @@ void ASTDeclReader::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
D->setExplicitSpecifier(Record.readExplicitSpec());
D->Ctor = readDeclAs<CXXConstructorDecl>();
VisitFunctionDecl(D);
- D->setIsCopyDeductionCandidate(Record.readInt());
+ D->setDeductionCandidateKind(
+ static_cast<DeductionCandidate>(Record.readInt()));
}
void ASTDeclReader::VisitCXXMethodDecl(CXXMethodDecl *D) {
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 36f882a30bbaca..59dbc36d24e8c9 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -730,7 +730,7 @@ void ASTDeclWriter::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
addExplicitSpecifier(D->getExplicitSpecifier(), Record);
Record.AddDeclRef(D->Ctor);
VisitFunctionDecl(D);
- Record.push_back(D->isCopyDeductionCandidate());
+ Record.push_back(static_cast<unsigned char>(D->getDeductionCandidateKind()));
Code = serialization::DECL_CXX_DEDUCTION_GUIDE;
}
diff --git a/clang/test/SemaTemplate/aggregate-deduction-candidate.cpp b/clang/test/SemaTemplate/aggregate-deduction-candidate.cpp
new file mode 100644
index 00000000000000..90794e012c6ee5
--- /dev/null
+++ b/clang/test/SemaTemplate/aggregate-deduction-candidate.cpp
@@ -0,0 +1,364 @@
+// RUN: %clang_cc1 -std=c++20 -verify -ast-dump -ast-dump-decl-types -ast-dump-filter "deduction guide" %s | FileCheck %s --strict-whitespace
+
+namespace Basic {
+ template<class T> struct A {
+ T x;
+ T y;
+ };
+
+ A a1 = {3.0, 4.0};
+ A a2 = {.x = 3.0, .y = 4.0};
+
+ A a3(3.0, 4.0);
+
+ // CHECK-LABEL: Dumping Basic::<deduction guide for A>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for A>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced class depth 0 index 0 T
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for A> 'auto (T, T) -> A<T>'
+ // CHECK: | |-ParmVarDecl {{.*}} 'T'
+ // CHECK: | `-ParmVarDecl {{.*}} 'T'
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for A> 'auto (double, double) -> Basic::A<double>'
+ // CHECK: |-TemplateArgument type 'double'
+ // CHECK: | `-BuiltinType {{.*}} 'double'
+ // CHECK: |-ParmVarDecl {{.*}} 'double':'double'
+ // CHECK: `-ParmVarDecl {{.*}} 'double':'double'
+ // CHECK: FunctionProtoType {{.*}} 'auto (T, T) -> A<T>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'A<T>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'A'
+ // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: | `-TemplateTypeParm {{.*}} 'T'
+ // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: `-TemplateTypeParm {{.*}} 'T'
+
+ template <typename T> struct S { // expected-note 2 {{candidate}}
+ T x;
+ T y;
+ };
+
+ template <typename T> struct C { // expected-note 10 {{candidate}}
+ S<T> s;
+ T t;
+ };
+
+ template <typename T> struct D { // expected-note 6 {{candidate}}
+ S<int> s;
+ T t;
+ };
+
+ C c1 = {1, 2}; // expected-error {{no viable}}
+ C c2 = {1, 2, 3}; // expected-error {{no viable}}
+ C c3 = {{1u, 2u}, 3};
+
+ C c4(1, 2); // expected-error {{no viable}}
+ C c5(1, 2, 3); // expected-error {{no viable}}
+ C c6({1u, 2u}, 3);
+
+ D d1 = {1, 2}; // expected-error {{no viable}}
+ D d2 = {1, 2, 3};
+
+ D d3(1, 2); // expected-error {{no viable}}
+ // CTAD succeed but brace elision is not allowed for parenthesized aggregate init.
+ D d4(1, 2, 3); // expected-error {{no viable}}
+
+ // CHECK-LABEL: Dumping Basic::<deduction guide for C>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for C>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for C> 'auto (S<T>, T) -> C<T>'
+ // CHECK: | |-ParmVarDecl {{.*}} 'S<T>':'S<T>'
+ // CHECK: | `-ParmVarDecl {{.*}} 'T'
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for C> 'auto (S<int>, int) -> Basic::C<int>'
+ // CHECK: |-TemplateArgument type 'int'
+ // CHECK: | `-BuiltinType {{.*}} 'int'
+ // CHECK: |-ParmVarDecl {{.*}} 'S<int>':'Basic::S<int>'
+ // CHECK: `-ParmVarDecl {{.*}} 'int':'int'
+ // CHECK: FunctionProtoType {{.*}} 'auto (S<T>, T) -> C<T>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'C<T>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'C'
+ // CHECK: |-ElaboratedType {{.*}} 'S<T>' sugar dependent
+ // CHECK: | `-TemplateSpecializationType {{.*}} 'S<T>' dependent S
+ // CHECK: | `-TemplateArgument type 'T'
+ // CHECK: | `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: | `-TemplateTypeParm {{.*}} 'T'
+ // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: `-TemplateTypeParm {{.*}} 'T'
+
+ // CHECK-LABEL: Dumping Basic::<deduction guide for D>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for D>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for D> 'auto (int, int) -> D<T>'
+ // CHECK: |-ParmVarDecl {{.*}} 'int':'int'
+ // CHECK: `-ParmVarDecl {{.*}} 'int':'int'
+ // CHECK: FunctionProtoType {{.*}} 'auto (int, int) -> D<T>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'D<T>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'D'
+ // CHECK: |-SubstTemplateTypeParmType {{.*}} 'int' sugar typename depth 0 index 0 T
+ // CHECK: | |-ClassTemplateSpecialization {{.*}} 'S'
+ // CHECK: | `-BuiltinType {{.*}} 'int'
+ // CHECK: `-SubstTemplateTypeParmType {{.*}} 'int' sugar typename depth 0 index 0 T
+ // CHECK: |-ClassTemplateSpecialization {{.*}} 'S'
+ // CHECK: `-BuiltinType {{.*}} 'int'
+
+ template <typename T> struct E {
+ T t;
+ decltype(t) t2;
+ };
+
+ E e1 = {1, 2};
+
+ E e2(1, 2);
+
+ // CHECK-LABEL: Dumping Basic::<deduction guide for E>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for E>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for E> 'auto (T, decltype(t)) -> E<T>'
+ // CHECK: | |-ParmVarDecl {{.*}} 'T'
+ // CHECK: | `-ParmVarDecl {{.*}} 'decltype(t)'
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for E> 'auto (int, decltype(t)) -> Basic::E<int>'
+ // CHECK: |-TemplateArgument type 'int'
+ // CHECK: | `-BuiltinType {{.*}} 'int'
+ // CHECK: |-ParmVarDecl {{.*}} 'int':'int'
+ // CHECK: `-ParmVarDecl {{.*}} 'decltype(t)':'int'
+ // CHECK: FunctionProtoType {{.*}} 'auto (T, decltype(t)) -> E<T>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'E<T>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'E'
+ // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: | `-TemplateTypeParm {{.*}} 'T'
+ // CHECK: `-DecltypeType {{.*}} 'decltype(t)' dependent
+ // CHECK: `-DeclRefExpr {{.*}} 'T' lvalue Field {{.*}} 't' 'T' non_odr_use_unevaluated
+
+ template <typename T>
+ struct I {
+ using type = T;
+ };
+
+ template <typename T>
+ struct F {
+ typename I<T>::type i;
+ T t;
+ };
+
+ F f1 = {1, 2};
+
+ // CHECK-LABEL: Dumping Basic::<deduction guide for F>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for F>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 'auto (typename I<T>::type, T) -> F<T>'
+ // CHECK: | |-ParmVarDecl {{.*}} 'typename I<T>::type'
+ // CHECK: | `-ParmVarDecl {{.*}} 'T'
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for F> 'auto (typename I<int>::type, int) -> Basic::F<int>'
+ // CHECK: |-TemplateArgument type 'int'
+ // CHECK: | `-BuiltinType {{.*}} 'int'
+ // CHECK: |-ParmVarDecl {{.*}} 'typename I<int>::type':'int'
+ // CHECK: `-ParmVarDecl {{.*}} 'int':'int'
+ // CHECK: FunctionProtoType {{.*}} 'auto (typename I<T>::type, T) -> F<T>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'F<T>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'F'
+ // CHECK: |-DependentNameType {{.*}} 'typename I<T>::type' dependent
+ // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: `-TemplateTypeParm {{.*}} 'T'
+}
+
+namespace Array {
+ typedef __SIZE_TYPE__ size_t;
+ template <typename T, size_t N> struct A { // expected-note 2 {{candidate}}
+ T array[N];
+ };
+
+ A a1 = {{1, 2, 3}};
+ A a2 = {1, 2, 3}; // expected-error {{no viable}}
+ A a3 = {"meow"};
+ A a4 = {("meow")};
+
+ A a5({1, 2, 3});
+ A a6("meow");
+ A a7(("meow"));
+
+ // CHECK-LABEL: Dumping Array::<deduction guide for A>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for A>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
+ // CHECK: |-NonTypeTemplateParmDecl {{.*}} 'size_t':'unsigned long{{( long)?}}' depth 0 index 1 N
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for A> 'auto (T (&&)[N]) -> A<T, N>'
+ // CHECK: | `-ParmVarDecl {{.*}} 'T (&&)[N]'
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for A> 'auto (int (&&)[3]) -> Array::A<int, 3>'
+ // CHECK: |-TemplateArgument type 'int'
+ // CHECK: | `-BuiltinType {{.*}} 'int'
+ // CHECK: |-TemplateArgument integral 3
+ // CHECK: `-ParmVarDecl {{.*}} 'int (&&)[3]'
+ // CHECK: FunctionProtoType {{.*}} 'auto (T (&&)[N]) -> A<T, N>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'A<T, N>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'A'
+ // CHECK: `-RValueReferenceType {{.*}} 'T (&&)[N]' dependent
+ // CHECK: `-DependentSizedArrayType {{.*}} 'T[N]' dependent
+ // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: | `-TemplateTypeParm {{.*}} 'T'
+ // CHECK: `-DeclRefExpr {{.*}} 'size_t':'unsigned long{{( long)?}}' NonTypeTemplateParm {{.*}} 'N' 'size_t':'unsigned long{{( long)?}}'
+
+ // CHECK: Dumping Array::<deduction guide for A>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for A>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
+ // CHECK: |-NonTypeTemplateParmDecl {{.*}} 'size_t':'unsigned long{{( long)?}}' depth 0 index 1 N
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for A> 'auto (T (&)[N]) -> A<T, N>'
+ // CHECK: | `-ParmVarDecl {{.*}} 'T (&)[N]'
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for A> 'auto (const char (&)[5]) -> Array::A<const char, 5>'
+ // CHECK: |-TemplateArgument type 'const char'
+ // CHECK: | `-QualType {{.*}} 'const char' const
+ // CHECK: | `-BuiltinType {{.*}} 'char'
+ // CHECK: |-TemplateArgument integral 5
+ // CHECK: `-ParmVarDecl {{.*}} 'const char (&)[5]'
+ // CHECK: FunctionProtoType {{.*}} 'auto (T (&)[N]) -> A<T, N>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'A<T, N>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'A'
+ // CHECK: `-LValueReferenceType {{.*}} 'T (&)[N]' dependent
+ // CHECK: `-DependentSizedArrayType {{.*}} 'T[N]' dependent
+ // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: | `-TemplateTypeParm {{.*}} 'T'
+ // CHECK: `-DeclRefExpr {{.*}} 'size_t':'unsigned long{{( long)?}}' NonTypeTemplateParm {{.*}} 'N' 'size_t':'unsigned long{{( long)?}}'
+}
+
+namespace BraceElision {
+ template <typename T> struct A {
+ T array[2];
+ };
+
+ A a1 = {0, 1};
+
+ // CTAD succeed but brace elision is not allowed for parenthesized aggregate init.
+ A a2(0, 1); // expected-error {{array initializer must be an initializer list}}
+
+ // CHECK-LABEL: Dumping BraceElision::<deduction guide for A>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for A>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for A> 'auto (T, T) -> A<T>'
+ // CHECK: | |-ParmVarDecl {{.*}} 'T'
+ // CHECK: | `-ParmVarDecl {{.*}} 'T'
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for A> 'auto (int, int) -> BraceElision::A<int>'
+ // CHECK: |-TemplateArgument type 'int'
+ // CHECK: | `-BuiltinType {{.*}} 'int'
+ // CHECK: |-ParmVarDecl {{.*}} 'int':'int'
+ // CHECK: `-ParmVarDecl {{.*}} 'int':'int'
+ // CHECK: FunctionProtoType {{.*}} 'auto (T, T) -> A<T>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'A<T>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'A'
+ // CHECK: |-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: | `-TemplateTypeParm {{.*}} 'T'
+ // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent depth 0 index 0
+ // CHECK: `-TemplateTypeParm {{.*}} 'T'
+}
+
+namespace TrailingPack {
+ template<typename... T> struct A : T... {
+ };
+
+ A a1 = {
+ []{ return 1; },
+ []{ return 2; }
+ };
+
+ A a2(
+ []{ return 1; },
+ []{ return 2; }
+ );
+
+ // CHECK-LABEL: Dumping TrailingPack::<deduction guide for A>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for A>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 ... T
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for A> 'auto (T...) -> A<T...>'
+ // CHECK: | `-ParmVarDecl {{.*}} 'T...' pack
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for A>
+ // CHECK-SAME: 'auto (TrailingPack::(lambda at {{.*}}), TrailingPack::(lambda at {{.*}})) ->
+ // CHECK-SAME: TrailingPack::A<TrailingPack::(lambda at {{.*}}), TrailingPack::(lambda at {{.*}})>'
+ // CHECK: |-TemplateArgument pack
+ // CHECK: | |-TemplateArgument type 'TrailingPack::(lambda at {{.*}})'
+ // CHECK: | | `-RecordType {{.*}} 'TrailingPack::(lambda at {{.*}})'
+ // CHECK: | | `-CXXRecord {{.*}} ''
+ // CHECK: | `-TemplateArgument type 'TrailingPack::(lambda at {{.*}})'
+ // CHECK: | `-RecordType {{.*}} 'TrailingPack::(lambda at {{.*}})'
+ // CHECK: | `-CXXRecord {{.*}} ''
+ // CHECK: |-ParmVarDecl {{.*}} 'TrailingPack::(lambda at {{.*}})':'TrailingPack::(lambda at {{.*}})'
+ // CHECK: `-ParmVarDecl {{.*}} 'TrailingPack::(lambda at {{.*}})':'TrailingPack::(lambda at {{.*}})'
+ // CHECK: FunctionProtoType {{.*}} 'auto (T...) -> A<T...>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'A<T...>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'A'
+ // CHECK: `-PackExpansionType {{.*}} 'T...' dependent
+ // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack
+ // CHECK: `-TemplateTypeParm {{.*}} 'T'
+}
+
+namespace NonTrailingPack {
+ template<typename... T> struct A : T... { // expected-note 4 {{candidate}}
+ int a;
+ };
+
+ A a1 = { // expected-error {{no viable}}
+ []{ return 1; },
+ []{ return 2; }
+ };
+
+ A a2( // expected-error {{no viable}}
+ []{ return 1; },
+ []{ return 2; }
+ );
+}
+
+namespace DeduceArity {
+ template <typename... T> struct Types {};
+ template <typename... T> struct F : Types<T...>, T... {}; // expected-note 12 {{candidate}}
+
+ struct X {};
+ struct Y {};
+ struct Z {};
+ struct W { operator Y(); };
+
+ F f1 = {Types<X, Y, Z>{}, {}, {}};
+ F f2 = {Types<X, Y, Z>{}, X{}, Y{}};
+ F f3 = {Types<X, Y, Z>{}, X{}, W{}}; // expected-error {{no viable}}
+ F f4 = {Types<X>{}, {}, {}}; // expected-error {{no viable}}
+
+ F f5(Types<X, Y, Z>{}, {}, {});
+ F f6(Types<X, Y, Z>{}, X{}, Y{});
+ F f7(Types<X, Y, Z>{}, X{}, W{}); // expected-error {{no viable}}
+ F f8(Types<X>{}, {}, {}); // expected-error {{no viable}}
+
+ // CHECK-LABEL: Dumping DeduceArity::<deduction guide for F>:
+ // CHECK: FunctionTemplateDecl {{.*}} implicit <deduction guide for F>
+ // CHECK: |-TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 ... T
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 'auto (Types<T...>, T...) -> F<T...>'
+ // CHECK: | |-ParmVarDecl {{.*}} 'Types<T...>':'Types<T...>'
+ // CHECK: | `-ParmVarDecl {{.*}} 'T...' pack
+ // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for F>
+ // CHECK-SAME: 'auto (Types<X, Y, Z>, DeduceArity::X, DeduceArity::Y, DeduceArity::Z) ->
+ // CHECK-SAME: DeduceArity::F<DeduceArity::X, DeduceArity::Y, DeduceArity::Z>'
+ // CHECK: | |-TemplateArgument pack
+ // CHECK: | | |-TemplateArgument type 'DeduceArity::X'
+ // CHECK: | | | `-RecordType {{.*}} 'DeduceArity::X'
+ // CHECK: | | | `-CXXRecord {{.*}} 'X'
+ // CHECK: | | |-TemplateArgument type 'DeduceArity::Y'
+ // CHECK: | | | `-RecordType {{.*}} 'DeduceArity::Y'
+ // CHECK: | | | `-CXXRecord {{.*}} 'Y'
+ // CHECK: | | `-TemplateArgument type 'DeduceArity::Z'
+ // CHECK: | | `-RecordType {{.*}} 'DeduceArity::Z'
+ // CHECK: | | `-CXXRecord {{.*}} 'Z'
+ // CHECK: | |-ParmVarDecl {{.*}} 'Types<X, Y, Z>':'DeduceArity::Types<DeduceArity::X, DeduceArity::Y, DeduceArity::Z>'
+ // CHECK: | |-ParmVarDecl {{.*}} 'DeduceArity::X':'DeduceArity::X'
+ // CHECK: | |-ParmVarDecl {{.*}} 'DeduceArity::Y':'DeduceArity::Y'
+ // CHECK: | `-ParmVarDecl {{.*}} 'DeduceArity::Z':'DeduceArity::Z'
+ // CHECK: `-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 'auto (Types<X>, DeduceArity::X) -> DeduceArity::F<DeduceArity::X>'
+ // CHECK: |-TemplateArgument pack
+ // CHECK: | `-TemplateArgument type 'DeduceArity::X'
+ // CHECK: | `-RecordType {{.*}} 'DeduceArity::X'
+ // CHECK: | `-CXXRecord {{.*}} 'X'
+ // CHECK: |-ParmVarDecl {{.*}} 'Types<X>':'DeduceArity::Types<DeduceArity::X>'
+ // CHECK: `-ParmVarDecl {{.*}} 'DeduceArity::X':'DeduceArity::X'
+ // CHECK: FunctionProtoType {{.*}} 'auto (Types<T...>, T...) -> F<T...>' dependent trailing_return cdecl
+ // CHECK: |-InjectedClassNameType {{.*}} 'F<T...>' dependent
+ // CHECK: | `-CXXRecord {{.*}} 'F'
+ // CHECK: |-ElaboratedType {{.*}} 'Types<T...>' sugar dependent
+ // CHECK: | `-TemplateSpecializationType {{.*}} 'Types<T...>' dependent Types
+ // CHECK: | `-TemplateArgument type 'T...'
+ // CHECK: | `-PackExpansionType {{.*}} 'T...' dependent
+ // CHECK: | `-TemplateTypeParmType {{.*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack
+ // CHECK: | `-TemplateTypeParm {{.*}} 'T'
+ // CHECK: `-PackExpansionType {{.*}} 'T...' dependent
+ // CHECK: `-TemplateTypeParmType {{.*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack
+ // CHECK: `-TemplateTypeParm {{.*}} 'T'
+}
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 8d8a8f58161bc2..ecf51d1c75c7d4 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -7372,7 +7372,7 @@ TEST_P(ImportFunctions, CTADImplicit) {
cxxDeductionGuideDecl(hasParameter(0, hasType(asString("A<T>")))));
auto *ToD = Import(FromD, Lang_CXX17);
ASSERT_TRUE(ToD);
- EXPECT_TRUE(ToD->isCopyDeductionCandidate());
+ EXPECT_EQ(ToD->getDeductionCandidateKind(), DeductionCandidate::Copy);
// Check that the deduced class template is also imported.
EXPECT_TRUE(findFromTU(FromD)->Importer->GetAlreadyImportedOrNull(
FromD->getDeducedTemplate()));
More information about the cfe-commits
mailing list