From 85d7bd3c86c4f7b800fe1c2c58f881ddd0290059 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 9 Feb 2017 19:17:44 +0000 Subject: [PATCH] [c++1z] P0091R3: Basic support for deducing class template arguments via deduction-guides. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@294613 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ExprCXX.h | 3 +- include/clang/AST/Type.h | 5 +- include/clang/Basic/DiagnosticSemaKinds.td | 23 +- include/clang/Sema/Initialization.h | 23 +- include/clang/Sema/Sema.h | 10 +- lib/AST/ExprCXX.cpp | 10 +- lib/Sema/SemaCast.cpp | 3 +- lib/Sema/SemaDecl.cpp | 103 ++++---- lib/Sema/SemaDeclCXX.cpp | 2 +- lib/Sema/SemaExprCXX.cpp | 92 ++++---- lib/Sema/SemaInit.cpp | 220 +++++++++++++++++- lib/Sema/SemaTemplateDeduction.cpp | 87 ++++--- test/CXX/expr/expr.post/expr.type.conv/p1.cpp | 10 + .../over.match.class.deduct/p3.cpp | 43 ++++ test/CXX/temp/temp.deduct.guide/p2.cpp | 3 +- ...xx1z-class-template-argument-deduction.cpp | 98 ++++---- ...xx1z-class-template-argument-deduction.cpp | 84 +++++++ 17 files changed, 630 insertions(+), 189 deletions(-) create mode 100644 test/CXX/expr/expr.post/expr.type.conv/p1.cpp create mode 100644 test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p3.cpp create mode 100644 test/SemaCXX/cxx1z-class-template-argument-deduction.cpp diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 37e59771a7..03da28acfe 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1470,7 +1470,8 @@ class CXXTemporaryObjectExpr : public CXXConstructExpr { public: CXXTemporaryObjectExpr(const ASTContext &C, CXXConstructorDecl *Cons, - TypeSourceInfo *Type, + QualType Type, + TypeSourceInfo *TSI, ArrayRef Args, SourceRange ParenOrBraceRange, bool HadMultipleCandidates, diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 06d34ad624..0a50b618c9 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -4115,7 +4115,10 @@ class DeducedType : public Type { protected: DeducedType(TypeClass TC, QualType DeducedAsType, bool IsDependent, bool IsInstantiationDependent, bool ContainsParameterPack) - : Type(TC, DeducedAsType.isNull() ? QualType(this, 0) : DeducedAsType, + : Type(TC, + // FIXME: Retain the sugared deduced type? + DeducedAsType.isNull() ? QualType(this, 0) + : DeducedAsType.getCanonicalType(), IsDependent, IsInstantiationDependent, /*VariablyModified=*/false, ContainsParameterPack) { if (!DeducedAsType.isNull()) { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index be6922171a..8a36e704c0 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1849,8 +1849,8 @@ def warn_cxx98_compat_temp_copy : Warning< InGroup, DefaultIgnore; def err_selected_explicit_constructor : Error< "chosen constructor is explicit in copy-initialization">; -def note_constructor_declared_here : Note< - "constructor declared here">; +def note_explicit_ctor_deduction_guide_here : Note< + "explicit %select{constructor|deduction guide}0 declared here">; // C++11 decltype def err_decltype_in_declarator : Error< @@ -1961,8 +1961,23 @@ def err_deduced_class_template_compound_type : Error< "cannot %select{form pointer to|form reference to|form array of|" "form function returning|use parentheses when declaring variable with}0 " "deduced class template specialization type">; -def err_deduced_class_template_not_supported : Error< - "deduction of template arguments for class templates is not yet supported">; +def err_deduced_non_class_template_specialization_type : Error< + "%select{|function template|variable template|alias template|" + "template template parameter|template}0 %1 requires template arguments; " + "argument deduction only allowed for class templates">; +def err_deduced_class_template_ctor_ambiguous : Error< + "ambiguous deduction for template arguments of %0">; +def err_deduced_class_template_ctor_no_viable : Error< + "no viable constructor or deduction guide for deduction of " + "template arguments of %0">; +def err_deduced_class_template_incomplete : Error< + "template %0 has no definition and no %select{|viable }1deduction guides " + "for deduction of template arguments">; +def err_deduced_class_template_deleted : Error< + "class template argument deduction for %0 selected a deleted constructor">; +def err_deduced_class_template_explicit : Error< + "class template argument deduction for %0 selected an explicit " + "%select{constructor|deduction guide}1 for copy-list-initialization">; def err_deduction_guide_no_trailing_return_type : Error< "deduction guide declaration without trailing return type">; def err_deduction_guide_with_complex_decl : Error< diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 94be58ac8a..7b13867cdf 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -274,15 +274,18 @@ public: /// \brief Create the initialization entity for a temporary. static InitializedEntity InitializeTemporary(QualType Type) { - InitializedEntity Result(EK_Temporary, SourceLocation(), Type); - Result.TypeInfo = nullptr; - return Result; + return InitializeTemporary(nullptr, Type); } /// \brief Create the initialization entity for a temporary. static InitializedEntity InitializeTemporary(TypeSourceInfo *TypeInfo) { - InitializedEntity Result(EK_Temporary, SourceLocation(), - TypeInfo->getType()); + return InitializeTemporary(TypeInfo, TypeInfo->getType()); + } + + /// \brief Create the initialization entity for a temporary. + static InitializedEntity InitializeTemporary(TypeSourceInfo *TypeInfo, + QualType Type) { + InitializedEntity Result(EK_Temporary, SourceLocation(), Type); Result.TypeInfo = TypeInfo; return Result; } @@ -579,6 +582,16 @@ public: return InitializationKind(IK_Value, isImplicit ? IC_Implicit : IC_Normal, InitLoc, LParenLoc, RParenLoc); } + + /// \brief Create an initialization from an initializer (which, for direct + /// initialization from a parenthesized list, will be a ParenListExpr). + static InitializationKind CreateForInit(SourceLocation Loc, bool DirectInit, + Expr *Init) { + if (!Init) return CreateDefault(Loc); + if (!DirectInit) return CreateCopy(Loc, Init->getLocStart()); + if (isa(Init)) return CreateDirectList(Loc); + return CreateDirect(Loc, Init->getLocStart(), Init->getLocEnd()); + } /// \brief Determine the initialization kind. InitKind getKind() const { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 5b317b018a..c42d216154 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1760,6 +1760,8 @@ public: // Returns true if the variable declaration is a redeclaration bool CheckVariableDeclaration(VarDecl *NewVD, LookupResult &Previous); void CheckVariableDeclarationType(VarDecl *NewVD); + bool DeduceVariableDeclarationType(VarDecl *VDecl, bool DirectInit, + Expr *Init); void CheckCompleteVariableDeclaration(VarDecl *VD); void CheckCompleteDecompositionDeclaration(DecompositionDecl *DD); void MaybeSuggestAddingStaticToDecl(const FunctionDecl *D); @@ -4340,7 +4342,7 @@ public: /// \brief Determine whether Ctor is an initializer-list constructor, as /// defined in [dcl.init.list]p2. - bool isInitListConstructor(const CXXConstructorDecl *Ctor); + bool isInitListConstructor(const FunctionDecl *Ctor); Decl *ActOnUsingDirective(Scope *CurScope, SourceLocation UsingLoc, @@ -6767,6 +6769,10 @@ public: bool DeduceReturnType(FunctionDecl *FD, SourceLocation Loc, bool Diagnose = true); + QualType DeduceTemplateSpecializationFromInitializer( + TypeSourceInfo *TInfo, const InitializedEntity &Entity, + const InitializationKind &Kind, MultiExprArg Init); + QualType deduceVarTypeFromInitializer(VarDecl *VDecl, DeclarationName Name, QualType Type, TypeSourceInfo *TSI, SourceRange Range, bool DirectInit, @@ -9304,7 +9310,7 @@ public: ExprResult CheckExtVectorCast(SourceRange R, QualType DestTy, Expr *CastExpr, CastKind &Kind); - ExprResult BuildCXXFunctionalCastExpr(TypeSourceInfo *TInfo, + ExprResult BuildCXXFunctionalCastExpr(TypeSourceInfo *TInfo, QualType Type, SourceLocation LParenLoc, Expr *CastExpr, SourceLocation RParenLoc); diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index ad510e0070..6713fca045 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -734,23 +734,23 @@ CXXBindTemporaryExpr *CXXBindTemporaryExpr::Create(const ASTContext &C, CXXTemporaryObjectExpr::CXXTemporaryObjectExpr(const ASTContext &C, CXXConstructorDecl *Cons, - TypeSourceInfo *Type, + QualType Type, + TypeSourceInfo *TSI, ArrayRef Args, SourceRange ParenOrBraceRange, bool HadMultipleCandidates, bool ListInitialization, bool StdInitListInitialization, bool ZeroInitialization) - : CXXConstructExpr(C, CXXTemporaryObjectExprClass, - Type->getType().getNonReferenceType(), - Type->getTypeLoc().getBeginLoc(), + : CXXConstructExpr(C, CXXTemporaryObjectExprClass, Type, + TSI->getTypeLoc().getBeginLoc(), Cons, false, Args, HadMultipleCandidates, ListInitialization, StdInitListInitialization, ZeroInitialization, CXXConstructExpr::CK_Complete, ParenOrBraceRange), - Type(Type) { + Type(TSI) { } SourceLocation CXXTemporaryObjectExpr::getLocStart() const { diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index 6f9852aaf5..39e85e76d6 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -2627,11 +2627,12 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc, } ExprResult Sema::BuildCXXFunctionalCastExpr(TypeSourceInfo *CastTypeInfo, + QualType Type, SourceLocation LPLoc, Expr *CastExpr, SourceLocation RPLoc) { assert(LPLoc.isValid() && "List-initialization shouldn't get here."); - CastOperation Op(*this, CastTypeInfo->getType(), CastExpr); + CastOperation Op(*this, Type, CastExpr); Op.DestRange = CastTypeInfo->getTypeLoc().getSourceRange(); Op.OpRange = SourceRange(Op.DestRange.getBegin(), CastExpr->getLocEnd()); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f103f30b4d..658146d638 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -9807,16 +9807,33 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl, DeducedType *Deduced = Type->getContainedDeducedType(); assert(Deduced && "deduceVarTypeFromInitializer for non-deduced type"); + ArrayRef DeduceInits = Init ? ArrayRef(Init) : None; + if (DirectInit) { + if (auto *PL = dyn_cast_or_null(Init)) + DeduceInits = PL->exprs(); + } + if (isa(Deduced)) { - Diag(Init->getLocStart(), diag::err_deduced_class_template_not_supported); + assert(VDecl && "non-auto type for init capture deduction?"); + InitializedEntity Entity = InitializedEntity::InitializeVariable(VDecl); + InitializationKind Kind = InitializationKind::CreateForInit( + VDecl->getLocation(), DirectInit, Init); + // FIXME: Initialization should not be taking a mutable list of inits. + SmallVector InitsCopy(DeduceInits.begin(), DeduceInits.end()); + return DeduceTemplateSpecializationFromInitializer(TSI, Entity, Kind, + InitsCopy); + } + + // C++11 [dcl.spec.auto]p3 + if (!Init) { + assert(VDecl && "no init for init capture deduction?"); + Diag(VDecl->getLocation(), diag::err_auto_var_requires_init) + << VDecl->getDeclName() << Type; return QualType(); } - ArrayRef DeduceInits = Init; if (DirectInit) { - if (auto *PL = dyn_cast(Init)) - DeduceInits = PL->exprs(); - else if (auto *IL = dyn_cast(Init)) + if (auto *IL = dyn_cast(Init)) DeduceInits = IL->inits(); } @@ -9902,6 +9919,36 @@ QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl, return DeducedType; } +bool Sema::DeduceVariableDeclarationType(VarDecl *VDecl, bool DirectInit, + Expr *Init) { + QualType DeducedType = deduceVarTypeFromInitializer( + VDecl, VDecl->getDeclName(), VDecl->getType(), VDecl->getTypeSourceInfo(), + VDecl->getSourceRange(), DirectInit, Init); + if (DeducedType.isNull()) { + VDecl->setInvalidDecl(); + return true; + } + + VDecl->setType(DeducedType); + assert(VDecl->isLinkageValid()); + + // In ARC, infer lifetime. + if (getLangOpts().ObjCAutoRefCount && inferObjCARCLifetime(VDecl)) + VDecl->setInvalidDecl(); + + // If this is a redeclaration, check that the type we just deduced matches + // the previously declared type. + if (VarDecl *Old = VDecl->getPreviousDecl()) { + // We never need to merge the type, because we cannot form an incomplete + // array of auto, nor deduce such a type. + MergeVarDeclTypes(VDecl, Old, /*MergeTypeWithPrevious*/ false); + } + + // Check the deduced type is valid for a variable declaration. + CheckVariableDeclarationType(VDecl); + return VDecl->isInvalidDecl(); +} + /// AddInitializerToDecl - Adds the initializer Init to the /// declaration dcl. If DirectInit is true, this is C++ direct /// initialization rather than copy initialization. @@ -9941,32 +9988,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { } Init = Res.get(); - QualType DeducedType = deduceVarTypeFromInitializer( - VDecl, VDecl->getDeclName(), VDecl->getType(), - VDecl->getTypeSourceInfo(), VDecl->getSourceRange(), DirectInit, Init); - if (DeducedType.isNull()) { - RealDecl->setInvalidDecl(); - return; - } - - VDecl->setType(DeducedType); - assert(VDecl->isLinkageValid()); - - // In ARC, infer lifetime. - if (getLangOpts().ObjCAutoRefCount && inferObjCARCLifetime(VDecl)) - VDecl->setInvalidDecl(); - - // If this is a redeclaration, check that the type we just deduced matches - // the previously declared type. - if (VarDecl *Old = VDecl->getPreviousDecl()) { - // We never need to merge the type, because we cannot form an incomplete - // array of auto, nor deduce such a type. - MergeVarDeclTypes(VDecl, Old, /*MergeTypeWithPrevious*/ false); - } - - // Check the deduced type is valid for a variable declaration. - CheckVariableDeclarationType(VDecl); - if (VDecl->isInvalidDecl()) + if (DeduceVariableDeclarationType(VDecl, DirectInit, Init)) return; } @@ -10085,15 +10107,8 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { } InitializedEntity Entity = InitializedEntity::InitializeVariable(VDecl); - InitializationKind Kind = - DirectInit - ? CXXDirectInit - ? InitializationKind::CreateDirect(VDecl->getLocation(), - Init->getLocStart(), - Init->getLocEnd()) - : InitializationKind::CreateDirectList(VDecl->getLocation()) - : InitializationKind::CreateCopy(VDecl->getLocation(), - Init->getLocStart()); + InitializationKind Kind = InitializationKind::CreateForInit( + VDecl->getLocation(), DirectInit, Init); MultiExprArg Args = Init; if (CXXDirectInit) @@ -10417,13 +10432,9 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { return; } - // C++11 [dcl.spec.auto]p3 - if (Type->isUndeducedType()) { - Diag(Var->getLocation(), diag::err_auto_var_requires_init) - << Var->getDeclName() << Type; - Var->setInvalidDecl(); + if (Type->isUndeducedType() && + DeduceVariableDeclarationType(Var, false, nullptr)) return; - } // C++11 [class.static.data]p3: A static data member can be declared with // the constexpr specifier; if so, its declaration shall specify diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 974e09f78d..1e0df80830 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -8556,7 +8556,7 @@ QualType Sema::BuildStdInitializerList(QualType Element, SourceLocation Loc) { CheckTemplateIdType(TemplateName(StdInitializerList), Loc, Args)); } -bool Sema::isInitListConstructor(const CXXConstructorDecl* Ctor) { +bool Sema::isInitListConstructor(const FunctionDecl *Ctor) { // C++ [dcl.init.list]p2: // A constructor is an initializer-list constructor if its first parameter // is of type std::initializer_list or reference to possibly cv-qualified diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 1185dacc22..7937654f27 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1266,15 +1266,6 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, RParenLoc); } - // C++1z [expr.type.conv]p1: - // If the type is a placeholder for a deduced class type, [...perform class - // template argument deduction...] - DeducedType *Deduced = Ty->getContainedDeducedType(); - if (Deduced && isa(Deduced)) { - Diag(TyBeginLoc, diag::err_deduced_class_template_not_supported); - return ExprError(); - } - bool ListInitialization = LParenLoc.isInvalid(); assert((!ListInitialization || (Exprs.size() == 1 && isa(Exprs[0]))) && @@ -1282,13 +1273,34 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, SourceRange FullRange = SourceRange(TyBeginLoc, ListInitialization ? Exprs[0]->getSourceRange().getEnd() : RParenLoc); + InitializedEntity Entity = InitializedEntity::InitializeTemporary(TInfo); + InitializationKind Kind = + Exprs.size() + ? ListInitialization + ? InitializationKind::CreateDirectList(TyBeginLoc) + : InitializationKind::CreateDirect(TyBeginLoc, LParenLoc, + RParenLoc) + : InitializationKind::CreateValue(TyBeginLoc, LParenLoc, RParenLoc); + + // C++1z [expr.type.conv]p1: + // If the type is a placeholder for a deduced class type, [...perform class + // template argument deduction...] + DeducedType *Deduced = Ty->getContainedDeducedType(); + if (Deduced && isa(Deduced)) { + Ty = DeduceTemplateSpecializationFromInitializer(TInfo, Entity, + Kind, Exprs); + if (Ty.isNull()) + return ExprError(); + Entity = InitializedEntity::InitializeTemporary(TInfo, Ty); + } + // C++ [expr.type.conv]p1: // If the expression list is a single expression, the type conversion // expression is equivalent (in definedness, and if defined in meaning) to the // corresponding cast expression. if (Exprs.size() == 1 && !ListInitialization) { Expr *Arg = Exprs[0]; - return BuildCXXFunctionalCastExpr(TInfo, LParenLoc, Arg, RParenLoc); + return BuildCXXFunctionalCastExpr(TInfo, Ty, LParenLoc, Arg, RParenLoc); } // C++14 [expr.type.conv]p2: The expression T(), where T is a @@ -1313,12 +1325,6 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, diag::err_invalid_incomplete_type_use, FullRange)) return ExprError(); - InitializedEntity Entity = InitializedEntity::InitializeTemporary(TInfo); - InitializationKind Kind = - Exprs.size() ? ListInitialization - ? InitializationKind::CreateDirectList(TyBeginLoc) - : InitializationKind::CreateDirect(TyBeginLoc, LParenLoc, RParenLoc) - : InitializationKind::CreateValue(TyBeginLoc, LParenLoc, RParenLoc); InitializationSequence InitSeq(*this, Entity, Kind, Exprs); ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Exprs); @@ -1339,7 +1345,7 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, // is sometimes handled by initialization and sometimes not. QualType ResultType = Result.get()->getType(); Result = CXXFunctionalCastExpr::Create( - Context, ResultType, Expr::getValueKindForType(TInfo->getType()), TInfo, + Context, ResultType, Expr::getValueKindForType(Ty), TInfo, CK_NoOp, Result.get(), /*Path=*/nullptr, LParenLoc, RParenLoc); } @@ -1665,13 +1671,38 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, NumInits = List->getNumExprs(); } + // C++11 [expr.new]p15: + // A new-expression that creates an object of type T initializes that + // object as follows: + InitializationKind Kind + // - If the new-initializer is omitted, the object is default- + // initialized (8.5); if no initialization is performed, + // the object has indeterminate value + = initStyle == CXXNewExpr::NoInit + ? InitializationKind::CreateDefault(TypeRange.getBegin()) + // - Otherwise, the new-initializer is interpreted according to the + // initialization rules of 8.5 for direct-initialization. + : initStyle == CXXNewExpr::ListInit + ? InitializationKind::CreateDirectList(TypeRange.getBegin()) + : InitializationKind::CreateDirect(TypeRange.getBegin(), + DirectInitRange.getBegin(), + DirectInitRange.getEnd()); + // C++11 [dcl.spec.auto]p6. Deduce the type which 'auto' stands in for. - if (AllocType->isUndeducedType()) { - if (isa( - AllocType->getContainedDeducedType())) - return ExprError(Diag(TypeRange.getBegin(), - diag::err_deduced_class_template_not_supported)); + auto *Deduced = AllocType->getContainedDeducedType(); + if (Deduced && isa(Deduced)) { + if (ArraySize) + return ExprError(Diag(ArraySize->getExprLoc(), + diag::err_deduced_class_template_compound_type) + << /*array*/ 2 << ArraySize->getSourceRange()); + InitializedEntity Entity + = InitializedEntity::InitializeNew(StartLoc, AllocType); + AllocType = DeduceTemplateSpecializationFromInitializer( + AllocTypeInfo, Entity, Kind, MultiExprArg(Inits, NumInits)); + if (AllocType.isNull()) + return ExprError(); + } else if (Deduced) { if (initStyle == CXXNewExpr::NoInit || NumInits == 0) return ExprError(Diag(StartLoc, diag::err_auto_new_requires_ctor_arg) << AllocType << TypeRange); @@ -1958,23 +1989,6 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, else InitType = AllocType; - // C++11 [expr.new]p15: - // A new-expression that creates an object of type T initializes that - // object as follows: - InitializationKind Kind - // - If the new-initializer is omitted, the object is default- - // initialized (8.5); if no initialization is performed, - // the object has indeterminate value - = initStyle == CXXNewExpr::NoInit - ? InitializationKind::CreateDefault(TypeRange.getBegin()) - // - Otherwise, the new-initializer is interpreted according to the - // initialization rules of 8.5 for direct-initialization. - : initStyle == CXXNewExpr::ListInit - ? InitializationKind::CreateDirectList(TypeRange.getBegin()) - : InitializationKind::CreateDirect(TypeRange.getBegin(), - DirectInitRange.getBegin(), - DirectInitRange.getEnd()); - InitializedEntity Entity = InitializedEntity::InitializeNew(StartLoc, InitType); InitializationSequence InitSeq(*this, Entity, Kind, diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 8a70d50a78..23ea650dd3 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -5914,7 +5914,8 @@ PerformConstructorInitialization(Sema &S, S.MarkFunctionReferenced(Loc, Constructor); CurInit = new (S.Context) CXXTemporaryObjectExpr( - S.Context, Constructor, TSInfo, + S.Context, Constructor, + Entity.getType().getNonLValueExprType(S.Context), TSInfo, ConstructorArgs, ParenOrBraceRange, HadMultipleCandidates, IsListInitialization, IsStdInitListInitialization, ConstructorInitRequiresZeroInit); @@ -6982,7 +6983,7 @@ InitializationSequence::Perform(Sema &S, Kind.getRange().getBegin()); CurInit = new (S.Context) CXXScalarValueInitExpr( - TSInfo->getType().getNonLValueExprType(S.Context), TSInfo, + Entity.getType().getNonLValueExprType(S.Context), TSInfo, Kind.getRange().getEnd()); } else { CurInit = new (S.Context) ImplicitValueInitExpr(Step->Type); @@ -7755,7 +7756,8 @@ bool InitializationSequence::Diagnose(Sema &S, (void)Ovl; assert(Ovl == OR_Success && "Inconsistent overload resolution"); CXXConstructorDecl *CtorDecl = cast(Best->Function); - S.Diag(CtorDecl->getLocation(), diag::note_constructor_declared_here); + S.Diag(CtorDecl->getLocation(), + diag::note_explicit_ctor_deduction_guide_here) << false; break; } } @@ -8219,3 +8221,215 @@ Sema::PerformCopyInitialization(const InitializedEntity &Entity, return Result; } + +QualType Sema::DeduceTemplateSpecializationFromInitializer( + TypeSourceInfo *TSInfo, const InitializedEntity &Entity, + const InitializationKind &Kind, MultiExprArg Inits) { + auto *DeducedTST = dyn_cast( + TSInfo->getType()->getContainedDeducedType()); + assert(DeducedTST && "not a deduced template specialization type"); + + // We can only perform deduction for class templates. + auto TemplateName = DeducedTST->getTemplateName(); + auto *Template = + dyn_cast_or_null(TemplateName.getAsTemplateDecl()); + if (!Template) { + Diag(Kind.getLocation(), + diag::err_deduced_non_class_template_specialization_type) + << (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName; + if (auto *TD = TemplateName.getAsTemplateDecl()) + Diag(TD->getLocation(), diag::note_template_decl_here); + return QualType(); + } + + // FIXME: Perform "exact type" matching first, per CWG discussion? + // Or implement this via an implied 'T(T) -> T' deduction guide? + + // FIXME: Do we need/want a std::initializer_list special case? + + // C++1z [over.match.class.deduct]p1: + // A set of functions and function templates is formed comprising: + bool HasDefaultConstructor = false; + SmallVector CtorsAndGuides; + CXXRecordDecl *Primary = Template->getTemplatedDecl(); + bool Complete = isCompleteType(TSInfo->getTypeLoc().getEndLoc(), + Context.getTypeDeclType(Primary)); + if (Complete) { + for (NamedDecl *D : LookupConstructors(Template->getTemplatedDecl())) { + // - For each constructor of the class template designated by the + // template-name, a function template [...] + auto Info = getConstructorInfo(D); + if (!Info.Constructor || Info.Constructor->isInvalidDecl()) + continue; + + // FIXME: Synthesize a deduction guide. + + if (Info.Constructor->isDefaultConstructor()) + HasDefaultConstructor = true; + } + } + + // - For each deduction-guide, a function or function template [...] + DeclarationNameInfo NameInfo( + Context.DeclarationNames.getCXXDeductionGuideName(Template), + TSInfo->getTypeLoc().getEndLoc()); + LookupResult Guides(*this, NameInfo, LookupOrdinaryName); + LookupQualifiedName(Guides, Template->getDeclContext()); + for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) { + auto *FD = dyn_cast(*I); + if (FD && FD->getMinRequiredArguments() == 0) + HasDefaultConstructor = true; + CtorsAndGuides.push_back(I.getPair()); + } + + // FIXME: Do not diagnose inaccessible deduction guides. The standard isn't + // clear on this, but they're not found by name so access does not apply. + Guides.suppressDiagnostics(); + + // Figure out if this is list-initialization. + InitListExpr *ListInit = + (Inits.size() == 1 && Kind.getKind() != InitializationKind::IK_Direct) + ? dyn_cast(Inits[0]) + : nullptr; + + // C++1z [over.match.class.deduct]p1: + // Initialization and overload resolution are performed as described in + // [dcl.init] and [over.match.ctor], [over.match.copy], or [over.match.list] + // (as appropriate for the type of initialization performed) for an object + // of a hypothetical class type, where the selected functions and function + // templates are considered to be the constructors of that class type + // + // Since we know we're initializing a class type of a type unrelated to that + // of the initializer, this reduces to something fairly reasonable. + OverloadCandidateSet Candidates(Kind.getLocation(), + OverloadCandidateSet::CSK_Normal); + OverloadCandidateSet::iterator Best; + auto tryToResolveOverload = + [&](bool OnlyListConstructors) -> OverloadingResult { + Candidates.clear(); + for (DeclAccessPair Pair : CtorsAndGuides) { + NamedDecl *D = Pair.getDecl()->getUnderlyingDecl(); + if (D->isInvalidDecl()) + continue; + + FunctionTemplateDecl *TD = dyn_cast(D); + FunctionDecl *FD = + TD ? TD->getTemplatedDecl() : dyn_cast(D); + if (!FD) + continue; + + // 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 (Kind.isCopyInit() && !ListInit) { + // FIXME: if (FD->isExplicit()) continue; + + // When looking for a converting constructor, deduction guides that + // could never be called with one argument are not interesting. + if (FD->getMinRequiredArguments() > 1 || + (FD->getNumParams() == 0 && !FD->isVariadic())) + continue; + } + + // 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(FD)) + continue; + + // 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(); + + // FIXME: These are definitely wrong in the non-deduction-guide case. + if (TD) + AddTemplateOverloadCandidate(TD, Pair, /*ExplicitArgs*/ nullptr, Inits, + Candidates, SuppressUserConversions); + else + AddOverloadCandidate(FD, Pair, Inits, Candidates, + SuppressUserConversions); + } + return Candidates.BestViableFunction(*this, Kind.getLocation(), Best); + }; + + OverloadingResult Result = OR_No_Viable_Function; + + // C++11 [over.match.list]p1, per DR1467: for list-initialization, first + // try initializer-list constructors. + if (ListInit) { + if (ListInit->getNumInits() || !HasDefaultConstructor) + Result = tryToResolveOverload(/*OnlyListConstructor*/true); + // Then unwrap the initializer list and try again considering all + // constructors. + Inits = MultiExprArg(ListInit->getInits(), ListInit->getNumInits()); + } + + // 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); + + switch (Result) { + case OR_Ambiguous: + Diag(Kind.getLocation(), diag::err_deduced_class_template_ctor_ambiguous) + << TemplateName; + // FIXME: For list-initialization candidates, it'd usually be better to + // list why they were not viable when given the initializer list itself as + // an argument. + Candidates.NoteCandidates(*this, OCD_ViableCandidates, Inits); + return QualType(); + + case OR_No_Viable_Function: + Diag(Kind.getLocation(), + Complete ? diag::err_deduced_class_template_ctor_no_viable + : diag::err_deduced_class_template_incomplete) + << TemplateName << !CtorsAndGuides.empty(); + Candidates.NoteCandidates(*this, OCD_AllCandidates, Inits); + return QualType(); + + case OR_Deleted: { + Diag(Kind.getLocation(), diag::err_deduced_class_template_deleted) + << TemplateName; + NoteDeletedFunction(Best->Function); + return QualType(); + } + + case OR_Success: + // C++ [over.match.list]p1: + // In copy-list-initialization, if an explicit constructor is chosen, the + // initialization is ill-formed. + if (Kind.isCopyInit() && ListInit && + false /*FIXME: Best->Function->isExplicit()*/) { + bool IsDeductionGuide = !Best->Function->isImplicit(); + Diag(Kind.getLocation(), diag::err_deduced_class_template_explicit) + << TemplateName << IsDeductionGuide; + Diag(Best->Function->getLocation(), + diag::note_explicit_ctor_deduction_guide_here) + << IsDeductionGuide; + return QualType(); + } + + // Make sure we didn't select an unusable deduction guide, and mark it + // as referenced. + DiagnoseUseOfDecl(Best->Function, Kind.getLocation()); + MarkFunctionReferenced(Kind.getLocation(), Best->Function); + break; + } + + // C++ [dcl.type.class.deduct]p1: + // The placeholder is replaced by the return type of the function selected + // by overload resolution for class template deduction. + return SubstAutoType(TSInfo->getType(), Best->Function->getReturnType()); +} diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 57957c0728..571ef55e4d 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -4019,17 +4019,26 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( } namespace { - /// Substitute the 'auto' type specifier within a type for a given replacement - /// type. - class SubstituteAutoTransform : - public TreeTransform { + /// Substitute the 'auto' specifier or deduced template specialization type + /// specifier within a type for a given replacement type. + class SubstituteDeducedTypeTransform : + public TreeTransform { QualType Replacement; - bool UseAutoSugar; + bool UseTypeSugar; public: - SubstituteAutoTransform(Sema &SemaRef, QualType Replacement, - bool UseAutoSugar = true) - : TreeTransform(SemaRef), - Replacement(Replacement), UseAutoSugar(UseAutoSugar) {} + SubstituteDeducedTypeTransform(Sema &SemaRef, QualType Replacement, + bool UseTypeSugar = true) + : TreeTransform(SemaRef), + Replacement(Replacement), UseTypeSugar(UseTypeSugar) {} + + QualType TransformDesugared(TypeLocBuilder &TLB, DeducedTypeLoc TL) { + assert(isa(Replacement) && + "unexpected unsugared replacement kind"); + QualType Result = Replacement; + TemplateTypeParmTypeLoc NewTL = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; + } QualType TransformAutoType(TypeLocBuilder &TLB, AutoTypeLoc TL) { // If we're building the type pattern to deduce against, don't wrap the @@ -4039,21 +4048,29 @@ namespace { // auto &&lref = lvalue; // must transform into "rvalue reference to T" not "rvalue reference to // auto type deduced as T" in order for [temp.deduct.call]p3 to apply. - if (!UseAutoSugar) { - assert(isa(Replacement) && - "unexpected unsugared replacement kind"); - QualType Result = Replacement; - TemplateTypeParmTypeLoc NewTL = - TLB.push(Result); - NewTL.setNameLoc(TL.getNameLoc()); - return Result; - } else { - QualType Result = SemaRef.Context.getAutoType( - Replacement, TL.getTypePtr()->getKeyword(), Replacement.isNull()); - AutoTypeLoc NewTL = TLB.push(Result); - NewTL.setNameLoc(TL.getNameLoc()); - return Result; - } + // + // FIXME: Is this still necessary? + if (!UseTypeSugar) + return TransformDesugared(TLB, TL); + + QualType Result = SemaRef.Context.getAutoType( + Replacement, TL.getTypePtr()->getKeyword(), Replacement.isNull()); + auto NewTL = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; + } + + QualType TransformDeducedTemplateSpecializationType( + TypeLocBuilder &TLB, DeducedTemplateSpecializationTypeLoc TL) { + if (!UseTypeSugar) + return TransformDesugared(TLB, TL); + + QualType Result = SemaRef.Context.getDeducedTemplateSpecializationType( + TL.getTypePtr()->getTemplateName(), + Replacement, Replacement.isNull()); + auto NewTL = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; } ExprResult TransformLambdaExpr(LambdaExpr *E) { @@ -4104,7 +4121,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, if (!DependentDeductionDepth && (Type.getType()->isDependentType() || Init->isTypeDependent())) { - Result = SubstituteAutoTransform(*this, QualType()).Apply(Type); + Result = SubstituteDeducedTypeTransform(*this, QualType()).Apply(Type); assert(!Result.isNull() && "substituting DependentTy can't fail"); return DAR_Succeeded; } @@ -4127,7 +4144,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, return DAR_FailedAlreadyDiagnosed; // FIXME: Support a non-canonical deduced type for 'auto'. Deduced = Context.getCanonicalType(Deduced); - Result = SubstituteAutoTransform(*this, Deduced).Apply(Type); + Result = SubstituteDeducedTypeTransform(*this, Deduced).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; return DAR_Succeeded; @@ -4152,7 +4169,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, Loc, Loc, TemplParamPtr, Loc, nullptr); QualType FuncParam = - SubstituteAutoTransform(*this, TemplArg, /*UseAutoSugar*/false) + SubstituteDeducedTypeTransform(*this, TemplArg, /*UseTypeSugar*/false) .Apply(Type); assert(!FuncParam.isNull() && "substituting template parameter for 'auto' failed"); @@ -4167,7 +4184,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, // might acquire a matching type in the instantiation. auto DeductionFailed = [&]() -> DeduceAutoResult { if (Init->isTypeDependent()) { - Result = SubstituteAutoTransform(*this, QualType()).Apply(Type); + Result = SubstituteDeducedTypeTransform(*this, QualType()).Apply(Type); assert(!Result.isNull() && "substituting DependentTy can't fail"); return DAR_Succeeded; } @@ -4215,7 +4232,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, return DAR_FailedAlreadyDiagnosed; } - Result = SubstituteAutoTransform(*this, DeducedType).Apply(Type); + Result = SubstituteDeducedTypeTransform(*this, DeducedType).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; @@ -4238,22 +4255,22 @@ QualType Sema::SubstAutoType(QualType TypeWithAuto, QualType TypeToReplaceAuto) { if (TypeToReplaceAuto->isDependentType()) TypeToReplaceAuto = QualType(); - return SubstituteAutoTransform(*this, TypeToReplaceAuto) + return SubstituteDeducedTypeTransform(*this, TypeToReplaceAuto) .TransformType(TypeWithAuto); } -TypeSourceInfo* Sema::SubstAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, - QualType TypeToReplaceAuto) { +TypeSourceInfo *Sema::SubstAutoTypeSourceInfo(TypeSourceInfo *TypeWithAuto, + QualType TypeToReplaceAuto) { if (TypeToReplaceAuto->isDependentType()) TypeToReplaceAuto = QualType(); - return SubstituteAutoTransform(*this, TypeToReplaceAuto) + return SubstituteDeducedTypeTransform(*this, TypeToReplaceAuto) .TransformType(TypeWithAuto); } QualType Sema::ReplaceAutoType(QualType TypeWithAuto, QualType TypeToReplaceAuto) { - return SubstituteAutoTransform(*this, TypeToReplaceAuto, - /*UseAutoSugar*/ false) + return SubstituteDeducedTypeTransform(*this, TypeToReplaceAuto, + /*UseTypeSugar*/ false) .TransformType(TypeWithAuto); } diff --git a/test/CXX/expr/expr.post/expr.type.conv/p1.cpp b/test/CXX/expr/expr.post/expr.type.conv/p1.cpp new file mode 100644 index 0000000000..1bdd52c037 --- /dev/null +++ b/test/CXX/expr/expr.post/expr.type.conv/p1.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +template struct A { + T t, u; +}; +template A(T, T) -> A; // expected-note {{deduced conflicting types for parameter 'T'}} +template A(A) -> A; // expected-note {{requires 1 argument, but 2 were provided}} + +A a = A{1, 2}; +A b = A{3, 4.0}; // expected-error {{no viable constructor or deduction guide}} diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p3.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p3.cpp new file mode 100644 index 0000000000..d3d98870ae --- /dev/null +++ b/test/CXX/over/over.match/over.match.funcs/over.match.class.deduct/p3.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +namespace std_example { + template struct A { // expected-note 2{{candidate}} + // FIXME: This is a bad way to diagnose redeclaration of a class member! + explicit A(const T &, ...) noexcept; // expected-note {{previous}} expected-note {{candidate}} + A(T &&, ...); // expected-error {{missing exception specification 'noexcept'}} + }; + + int i; + // FIXME: All but the first should be valid once we synthesize deduction guides from constructors. + A a1 = {i, i}; // expected-error {{no viable constructor or deduction guide}} + A a2{i, i}; // expected-error {{no viable constructor or deduction guide}} + A a3{0, i}; // expected-error {{no viable constructor or deduction guide}} + A a4 = {0, i}; // expected-error {{no viable constructor or deduction guide}} + + template A(const T &, const T &) -> A; + template explicit A(T &&, T &&) -> A; + + A a5 = {0, 1}; // FIXME: Should be invalid, explicit deduction guide selected in copy-list-init + A a6{0, 1}; + A a7 = {0, i}; // expected-note {{in instantiation of}} + A a8{0, i}; // expected-error {{no matching constructor}} + + template struct B { + template using TA = T; + template B(U, TA); + }; + // FIXME: This is valid. + B b{(int *)0, (char *)0}; // expected-error {{no viable constructor or deduction guide}} +} + +namespace check { + using namespace std_example; + template constexpr bool same = false; + template constexpr bool same = true; + + static_assert(same>); + static_assert(same>); + static_assert(same>); + static_assert(same>); + static_assert(same>); +} diff --git a/test/CXX/temp/temp.deduct.guide/p2.cpp b/test/CXX/temp/temp.deduct.guide/p2.cpp index ef45429b1a..3549755ff0 100644 --- a/test/CXX/temp/temp.deduct.guide/p2.cpp +++ b/test/CXX/temp/temp.deduct.guide/p2.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -std=c++1z -verify %s +// expected-no-diagnostics namespace std_example { template struct S { @@ -10,5 +11,5 @@ namespace std_example { using type = short; operator type(); }; - S x{A()}; // expected-error {{not yet supported}} + S x{A()}; } diff --git a/test/Parser/cxx1z-class-template-argument-deduction.cpp b/test/Parser/cxx1z-class-template-argument-deduction.cpp index 65b1105d34..10be123eb0 100644 --- a/test/Parser/cxx1z-class-template-argument-deduction.cpp +++ b/test/Parser/cxx1z-class-template-argument-deduction.cpp @@ -1,6 +1,12 @@ // RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -verify %s -template struct A {}; // expected-note 35{{declared here}} +template struct A { // expected-note 35{{declared here}} + constexpr A() {} + constexpr A(int) {} + constexpr operator int() { return 0; } +}; +A() -> A; +A(int) -> A; // Make sure we still correctly parse cases where a template can appear without arguments. namespace template_template_arg { @@ -18,7 +24,7 @@ namespace template_template_arg { template struct YCCD {}; // expected-error {{requires template arguments}} // FIXME: replacing the invalid type with 'int' here is horrible - template ()> class C { }; // expected-error {{requires template arguments}} expected-error {{not implicitly convertible to 'int'}} + template ()> class C { }; // expected-error {{requires template arguments}} template struct G { }; // expected-error {{requires template arguments}} } @@ -27,7 +33,7 @@ namespace injected_class_name { A(T); void f(int) { A a = 1; - injected_class_name::A b = 1; // expected-error {{not yet supported}} + injected_class_name::A b = 1; // expected-error {{no viable constructor or deduction guide}} } void f(T); }; @@ -46,8 +52,8 @@ struct member { operator A(); // expected-error {{requires template arguments; argument deduction not allowed in conversion function type}} - static A x; // expected-error {{requires an initializer}} - static A y = 0; // expected-error {{not yet supported}} + static A x; // FIXME: We deduce A from the initializer despite this not being a definition! + static constexpr A y = 0; }; namespace in_typedef { @@ -67,18 +73,18 @@ namespace stmt { // simple-declaration or cast. We also permit it in conditions, // for-range-declarations, member-declarations for static data members, and // new-expressions, because not doing so would be bizarre. - A local = 0; // expected-error {{not yet supported}} - static A local_static = 0; // expected-error {{not yet supported}} - static thread_local A thread_local_static = 0; // expected-error {{not yet supported}} - if (A a = 0) {} // expected-error {{not yet supported}} - if (A a = 0; a) {} // expected-error {{not yet supported}} - switch (A a = 0) {} // expected-error {{not yet supported}} - switch (A a = 0; a) {} // expected-error {{not yet supported}} - for (A a = 0; a; /**/) {} // expected-error {{not yet supported}} - for (/**/; A a = 0; /**/) {} // expected-error {{not yet supported}} - while (A a = 0) {} // expected-error {{not yet supported}} + A local = 0; + static A local_static = 0; + static thread_local A thread_local_static = 0; + if (A a = 0) {} + if (A a = 0; a) {} + switch (A a = 0) {} // expected-warning {{no case matching constant switch condition '0'}} + switch (A a = 0; a) {} // expected-warning {{no case matching constant switch condition '0'}} + for (A a = 0; a; /**/) {} + for (/**/; A a = 0; /**/) {} + while (A a = 0) {} int arr[3]; - for (A a : arr) {} // expected-error {{not yet supported}} + for (A a : arr) {} } namespace std { @@ -104,12 +110,12 @@ namespace expr { (void)(A)(n); // expected-error{{requires template arguments; argument deduction not allowed here}} (void)(A){n}; // expected-error{{requires template arguments; argument deduction not allowed here}} - (void)A(n); // expected-error {{not yet supported}} - (void)A{n}; // expected-error {{not yet supported}} - (void)new A(n); // expected-error {{not yet supported}} - (void)new A{n}; // expected-error {{not yet supported}} + (void)A(n); + (void)A{n}; + (void)new A(n); + (void)new A{n}; // FIXME: We should diagnose the lack of an initializer here. - (void)new A; // expected-error {{not yet supported}} + (void)new A; } } @@ -121,55 +127,57 @@ namespace decl { auto k() -> A; // expected-error{{requires template arguments}} - A a; // expected-error {{requires an initializer}} - A b = 0; // expected-error {{not yet supported}} - const A c = 0; // expected-error {{not yet supported}} + A a; // FIXME: This is (technically) syntactically invalid. + A b = 0; + const A c = 0; A (parens) = 0; // expected-error {{cannot use parentheses when declaring variable with deduced class template specialization type}} A *p = 0; // expected-error {{cannot form pointer to deduced class template specialization type}} A &r = *p; // expected-error {{cannot form reference to deduced class template specialization type}} A arr[3] = 0; // expected-error {{cannot form array of deduced class template specialization type}} A F::*pm = 0; // expected-error {{cannot form pointer to deduced class template specialization type}} A (*fp)() = 0; // expected-error {{cannot form function returning deduced class template specialization type}} - A [x, y] = 0; // expected-error {{cannot be declared with type 'A'}} expected-error {{not yet supported}} + A [x, y] = 0; // expected-error {{cannot be declared with type 'A'}} expected-error {{type 'A' decomposes into 0 elements, but 2 names were provided}} } namespace typename_specifier { struct F {}; void e() { - (void) typename ::A(0); // expected-error {{not yet supported}} - (void) typename ::A{0}; // expected-error {{not yet supported}} - new typename ::A(0); // expected-error {{not yet supported}} - new typename ::A{0}; // expected-error {{not yet supported}} - typename ::A a = 0; // expected-error {{not yet supported}} - const typename ::A b = 0; // expected-error {{not yet supported}} - if (typename ::A a = 0) {} // expected-error {{not yet supported}} - for (typename ::A a = 0; typename ::A b = 0; /**/) {} // expected-error 2{{not yet supported}} + (void) typename ::A(0); + (void) typename ::A{0}; + new typename ::A(0); + new typename ::A{0}; + typename ::A a = 0; + const typename ::A b = 0; + if (typename ::A a = 0) {} + for (typename ::A a = 0; typename ::A b = 0; /**/) {} (void)(typename ::A)(0); // expected-error{{requires template arguments; argument deduction not allowed here}} (void)(typename ::A){0}; // expected-error{{requires template arguments; argument deduction not allowed here}} } - typename ::A a = 0; // expected-error {{not yet supported}} - const typename ::A b = 0; // expected-error {{not yet supported}} + typename ::A a = 0; + const typename ::A b = 0; typename ::A (parens) = 0; // expected-error {{cannot use parentheses when declaring variable with deduced class template specialization type}} typename ::A *p = 0; // expected-error {{cannot form pointer to deduced class template specialization type}} typename ::A &r = *p; // expected-error {{cannot form reference to deduced class template specialization type}} typename ::A arr[3] = 0; // expected-error {{cannot form array of deduced class template specialization type}} typename ::A F::*pm = 0; // expected-error {{cannot form pointer to deduced class template specialization type}} typename ::A (*fp)() = 0; // expected-error {{cannot form function returning deduced class template specialization type}} - typename ::A [x, y] = 0; // expected-error {{cannot be declared with type 'typename ::A'}} expected-error {{not yet supported}} + typename ::A [x, y] = 0; // expected-error {{cannot be declared with type 'typename ::A'}} expected-error {{type 'typename ::A' (aka 'A') decomposes into 0}} struct X { template struct A {}; }; // expected-note 8{{template}} + // FIXME: We do not yet properly support class template argument deduction + // during template instantiation. template void f() { - (void) typename T::A(0); // expected-error {{not yet supported}} - (void) typename T::A{0}; // expected-error {{not yet supported}} - new typename T::A(0); // expected-error {{not yet supported}} - new typename T::A{0}; // expected-error {{not yet supported}} - typename T::A a = 0; // expected-error {{not yet supported}} - const typename T::A b = 0; // expected-error {{not yet supported}} - if (typename T::A a = 0) {} // expected-error {{not yet supported}} - for (typename T::A a = 0; typename T::A b = 0; /**/) {} // expected-error 2{{not yet supported}} + (void) typename T::A(0); // expected-error {{no viable}} + (void) typename T::A{0}; // expected-error {{no viable}} + new typename T::A(0); // expected-error {{no viable}} + new typename T::A{0}; // expected-error {{no viable}} + typename T::A a = 0; // expected-error {{no viable}} + const typename T::A b = 0; // expected-error {{no viable}} + if (typename T::A a = 0) {} // expected-error {{no viable}} + for (typename T::A a = 0; typename T::A b = 0; /**/) {} // expected-error 2{{no viable}} {(void)(typename T::A)(0);} // expected-error{{refers to class template member}} {(void)(typename T::A){0};} // expected-error{{refers to class template member}} @@ -179,7 +187,7 @@ namespace typename_specifier { {typename T::A arr[3] = 0;} // expected-error {{refers to class template member}} {typename T::A F::*pm = 0;} // expected-error {{refers to class template member}} {typename T::A (*fp)() = 0;} // expected-error {{refers to class template member}} - {typename T::A [x, y] = 0;} // expected-error {{cannot be declared with type 'typename T::A'}} expected-error {{not yet supported}} + {typename T::A [x, y] = 0;} // expected-error {{cannot be declared with type 'typename T::A'}} expected-error {{no viable}} } template void f(); // expected-note {{instantiation of}} diff --git a/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp b/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp new file mode 100644 index 0000000000..55aa6c6b14 --- /dev/null +++ b/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -std=c++1z -verify %s + +namespace std { + using size_t = decltype(sizeof(0)); + template struct initializer_list { + const T *p; + size_t n; + initializer_list(); + }; + // FIXME: This should probably not be necessary. + template initializer_list(initializer_list) -> initializer_list; +} + +template constexpr bool has_type(...) { return false; } +template constexpr bool has_type(T) { return true; } + +std::initializer_list il = {1, 2, 3, 4, 5}; + +template struct vector { + template vector(Iter, Iter); + vector(std::initializer_list); +}; + +template vector(std::initializer_list) -> vector; +template explicit vector(Iter, Iter) -> vector; +template explicit vector(std::size_t, T) -> vector; + +vector v1 = {1, 2, 3, 4}; +static_assert(has_type>(v1)); + +struct iter { typedef char value_type; } it, end; +vector v2(it, end); +static_assert(has_type>(v2)); + +vector v3(5, 5); +static_assert(has_type>(v3)); + + +template struct tuple { tuple(T...); }; +template explicit tuple(T ...t) -> tuple; +// FIXME: Remove +template tuple(tuple) -> tuple; + +const int n = 4; +tuple ta = tuple{1, 'a', "foo", n}; +static_assert(has_type>(ta)); + +tuple tb{ta}; +static_assert(has_type>(ta)); + +// FIXME: This should be tuple>; +tuple tc = {ta}; +static_assert(has_type>(ta)); + +tuple td = {1, 2, 3}; +static_assert(has_type>(ta)); + +// FIXME: This is a GCC extension for now; if CWG don't allow this, at least +// add a warning for it. +namespace new_expr { + tuple *p = new tuple{0}; + tuple *q = new tuple(1.0f, 2.0f); +} + +namespace ambiguity { + template struct A {}; + A(unsigned short) -> A; // expected-note {{candidate}} + A(short) -> A; // expected-note {{candidate}} + A a = 0; // expected-error {{ambiguous deduction for template arguments of 'A'}} + + template struct B {}; + template B(T(&)(int)) -> B; // expected-note {{candidate function [with T = int]}} + template B(int(&)(T)) -> B; // expected-note {{candidate function [with T = int]}} + int f(int); + B b = f; // expected-error {{ambiguous deduction for template arguments of 'B'}} +} + +// FIXME: Revisit this once CWG decides if attributes, and [[deprecated]] in +// particular, should be permitted here. +namespace deprecated { + template struct A { A(int); }; + [[deprecated]] A(int) -> A; // expected-note {{marked deprecated here}} + A a = 0; // expected-warning {{'' is deprecated}} +} -- 2.40.0