From 62b7cfb73e202051e7ab0dad42ba213acd0dec7e Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Tue, 17 Jan 2012 22:50:08 +0000 Subject: [PATCH] Auto deduction support for std::initializer_list, including for-range support. This means you can now write: for (int i : {1, 4, 512, 23, 251}) {} git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@148353 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 7 +++ include/clang/Sema/Sema.h | 7 +++ lib/Sema/SemaDecl.cpp | 4 +- lib/Sema/SemaDeclCXX.cpp | 52 +++++++++++++++++-- lib/Sema/SemaStmt.cpp | 2 +- lib/Sema/SemaTemplateDeduction.cpp | 40 +++++++++++--- .../cxx0x-initializer-stdinitializerlist.cpp | 8 +++ test/SemaCXX/generalized-initializers.cpp | 12 ----- 8 files changed, 107 insertions(+), 25 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index a13eedebfa..9b741101f4 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1196,10 +1196,17 @@ def err_trailing_return_in_parens : Error< "trailing return type may not be nested within parentheses">; def err_auto_var_deduction_failure : Error< "variable %0 with type %1 has incompatible initializer of type %2">; +def err_auto_var_deduction_failure_from_init_list : Error< + "cannot deduce actual type for variable %0 with type %1 from initializer list">; def err_auto_new_deduction_failure : Error< "new expression for type %0 has incompatible constructor argument of type %1">; def err_auto_different_deductions : Error< "'auto' deduced as %0 in declaration of %1 and deduced as %2 in declaration of %3">; +def err_implied_std_initializer_list_not_found : Error< + "cannot deduce type of initializer list because std::initializer_list was " + "not found; include ">; +def err_malformed_std_initializer_list : Error< + "std::initializer_list must be a class template with a single type parameter">; // C++11 override control def override_keyword_only_allowed_on_virtual_member_functions : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index fdc525ccc9..7e5b4e80f7 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2729,6 +2729,12 @@ public: /// it is and Element is not NULL, assigns the element type to Element. bool isStdInitializerList(QualType Ty, QualType *Element); + /// \brief Looks for the std::initializer_list template and instantiates it + /// with Element, or emits an error if it's not found. + /// + /// \returns The instantiated template, or null on error. + QualType BuildStdInitializerList(QualType Element, SourceLocation Loc); + Decl *ActOnUsingDirective(Scope *CurScope, SourceLocation UsingLoc, SourceLocation NamespcLoc, @@ -4707,6 +4713,7 @@ public: bool DeduceAutoType(TypeSourceInfo *AutoType, Expr *&Initializer, TypeSourceInfo *&Result); + void DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init); FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 0e99f70476..61e104fd1d 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5976,9 +5976,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, if (TypeMayContainAuto && VDecl->getType()->getContainedAutoType()) { TypeSourceInfo *DeducedType = 0; if (!DeduceAutoType(VDecl->getTypeSourceInfo(), Init, DeducedType)) - Diag(VDecl->getLocation(), diag::err_auto_var_deduction_failure) - << VDecl->getDeclName() << VDecl->getType() << Init->getType() - << Init->getSourceRange(); + DiagnoseAutoDeductionFailure(VDecl, Init); if (!DeducedType) { RealDecl->setInvalidDecl(); return; diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 13face95e9..2d47e0e3e8 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -5829,6 +5829,54 @@ bool Sema::isStdInitializerList(QualType Ty, QualType *Element) { return true; } +static ClassTemplateDecl *LookupStdInitializerList(Sema &S, SourceLocation Loc){ + NamespaceDecl *Std = S.getStdNamespace(); + if (!Std) { + S.Diag(Loc, diag::err_implied_std_initializer_list_not_found); + return 0; + } + + LookupResult Result(S, &S.PP.getIdentifierTable().get("initializer_list"), + Loc, Sema::LookupOrdinaryName); + if (!S.LookupQualifiedName(Result, Std)) { + S.Diag(Loc, diag::err_implied_std_initializer_list_not_found); + return 0; + } + ClassTemplateDecl *Template = Result.getAsSingle(); + if (!Template) { + Result.suppressDiagnostics(); + // We found something weird. Complain about the first thing we found. + NamedDecl *Found = *Result.begin(); + S.Diag(Found->getLocation(), diag::err_malformed_std_initializer_list); + return 0; + } + + // We found some template called std::initializer_list. Now verify that it's + // correct. + TemplateParameterList *Params = Template->getTemplateParameters(); + if (Params->size() != 1 || !isa(Params->getParam(0))) { + S.Diag(Template->getLocation(), diag::err_malformed_std_initializer_list); + return 0; + } + + return Template; +} + +QualType Sema::BuildStdInitializerList(QualType Element, SourceLocation Loc) { + if (!StdInitializerList) { + StdInitializerList = LookupStdInitializerList(*this, Loc); + if (!StdInitializerList) + return QualType(); + } + + TemplateArgumentListInfo Args(Loc, Loc); + Args.addArgument(TemplateArgumentLoc(TemplateArgument(Element), + Context.getTrivialTypeSourceInfo(Element, + Loc))); + return Context.getCanonicalType( + CheckTemplateIdType(TemplateName(StdInitializerList), Loc, Args)); +} + /// \brief Determine whether a using statement is in a context where it will be /// apply in all contexts. static bool IsUsingDirectiveInToplevelContext(DeclContext *CurContext) { @@ -9027,9 +9075,7 @@ void Sema::AddCXXDirectInitializerToDecl(Decl *RealDecl, Expr *Init = Exprs.get()[0]; TypeSourceInfo *DeducedType = 0; if (!DeduceAutoType(VDecl->getTypeSourceInfo(), Init, DeducedType)) - Diag(VDecl->getLocation(), diag::err_auto_var_deduction_failure) - << VDecl->getDeclName() << VDecl->getType() << Init->getType() - << Init->getSourceRange(); + DiagnoseAutoDeductionFailure(VDecl, Init); if (!DeducedType) { RealDecl->setInvalidDecl(); return; diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 78a58964db..23dc7e9d20 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -1164,7 +1164,7 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init, // Deduce the type for the iterator variable now rather than leaving it to // AddInitializerToDecl, so we can produce a more suitable diagnostic. TypeSourceInfo *InitTSI = 0; - if (Init->getType()->isVoidType() || + if ((!isa(Init) && Init->getType()->isVoidType()) || !SemaRef.DeduceAutoType(Decl->getTypeSourceInfo(), Init, InitTSI)) SemaRef.Diag(Loc, diag) << Init->getType(); if (!InitTSI) { diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 738e596ef3..4fff827206 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -3422,19 +3422,36 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, return false; TemplateDeductionInfo Info(Context, Loc); - if (DeduceTemplateArgumentsByTypeMatch(*this, &TemplateParams, FuncParam, - InitType, Info, Deduced, TDF)) - return false; + + InitListExpr * InitList = dyn_cast(Init); + if (InitList) { + for (unsigned i = 0, e = InitList->getNumInits(); i < e; ++i) { + if (DeduceTemplateArgumentsByTypeMatch(*this, &TemplateParams, FuncParam, + InitList->getInit(i)->getType(), + Info, Deduced, TDF)) + return false; + } + } else { + if (DeduceTemplateArgumentsByTypeMatch(*this, &TemplateParams, FuncParam, + InitType, Info, Deduced, TDF)) + return false; + } QualType DeducedType = Deduced[0].getAsType(); if (DeducedType.isNull()) return false; - + + if (InitList) { + DeducedType = BuildStdInitializerList(DeducedType, Loc); + if (DeducedType.isNull()) + return false; + } + Result = SubstituteAutoTransform(*this, DeducedType).TransformType(Type); - + // Check that the deduced argument type is compatible with the original // argument type per C++ [temp.deduct.call]p4. - if (Result && + if (!InitList && Result && CheckOriginalCallArgDeduction(*this, Sema::OriginalCallArg(FuncParam,0,InitType), Result->getType())) { @@ -3445,6 +3462,17 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, return true; } +void Sema::DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init) { + if (isa(Init)) + Diag(VDecl->getLocation(), + diag::err_auto_var_deduction_failure_from_init_list) + << VDecl->getDeclName() << VDecl->getType() << Init->getSourceRange(); + else + Diag(VDecl->getLocation(), diag::err_auto_var_deduction_failure) + << VDecl->getDeclName() << VDecl->getType() << Init->getType() + << Init->getSourceRange(); +} + static void MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, bool OnlyDeduced, diff --git a/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp index a08a04806c..47ceaf0b83 100644 --- a/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp +++ b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -109,3 +109,11 @@ void argument_deduction() { deduce_ref({1, 2.0}); // expected-error {{no matching function}} } + +void auto_deduction() { + auto l = {1, 2, 3, 4}; + static_assert(same_type>::value, ""); + auto bl = {1, 2.0}; // expected-error {{cannot deduce}} + + for (int i : {1, 2, 3, 4}) {} +} diff --git a/test/SemaCXX/generalized-initializers.cpp b/test/SemaCXX/generalized-initializers.cpp index 2e7df9aeaf..d696650e3e 100644 --- a/test/SemaCXX/generalized-initializers.cpp +++ b/test/SemaCXX/generalized-initializers.cpp @@ -38,18 +38,6 @@ namespace std { }; } -namespace integral { - - void initializer_list() { - auto l = {1, 2, 3, 4}; - static_assert(same_type>::value, ""); - auto bl = {1, 2.0}; // expected-error {{cannot deduce}} - - for (int i : {1, 2, 3, 4}) {} - } - -} - namespace objects { struct X1 { X1(int); }; -- 2.40.0