Author: rsmith Date: Thu Mar 23 20:14:25 2017 New Revision: 298676 URL: http://llvm.org/viewvc/llvm-project?rev=298676&view=rev Log: Fix handling of initialization from parenthesized initializer list.
This change fixes a crash on initialization of a reference from ({}) during template instantiation and incidentally improves diagnostics. This reverts a prior attempt to handle this in r286721. Instead, we teach the initialization code that initialization cannot be performed if a source type is required and the initializer is an initializer list (which is not an expression and does not have a type), and likewise for function-style cast expressions. Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Sema/Initialization.h cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/Sema/SemaDecl.cpp cfe/trunk/lib/Sema/SemaExprCXX.cpp cfe/trunk/lib/Sema/SemaInit.cpp cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp cfe/trunk/test/SemaCXX/cxx0x-initializer-scalars.cpp cfe/trunk/test/SemaCXX/type-convert-construct.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Mar 23 20:14:25 2017 @@ -1814,8 +1814,9 @@ def note_uninit_fixit_remove_cond : Note "remove the %select{'%1' if its condition|condition if it}0 " "is always %select{false|true}2">; def err_init_incomplete_type : Error<"initialization of incomplete type %0">; -def err_list_init_in_parens : Error<"list-initializer for non-class type %0 " - "must not be parenthesized">; +def err_list_init_in_parens : Error< + "cannot initialize %select{non-class|reference}0 type %1 with a " + "parenthesized initializer list">; def warn_unsequenced_mod_mod : Warning< "multiple unsequenced modifications to %0">, InGroup<Unsequenced>; @@ -5865,8 +5866,8 @@ def err_builtin_func_cast_more_than_one_ "function-style cast to a builtin type can only take one argument">; def err_value_init_for_array_type : Error< "array types cannot be value-initialized">; -def err_value_init_for_function_type : Error< - "function types cannot be value-initialized">; +def err_init_for_function_type : Error< + "cannot create object of function type %0">; def warn_format_nonliteral_noargs : Warning< "format string is not a string literal (potentially insecure)">, InGroup<FormatSecurity>; Modified: cfe/trunk/include/clang/Sema/Initialization.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Initialization.h?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Initialization.h (original) +++ cfe/trunk/include/clang/Sema/Initialization.h Thu Mar 23 20:14:25 2017 @@ -822,6 +822,8 @@ public: enum FailureKind { /// \brief Too many initializers provided for a reference. FK_TooManyInitsForReference, + /// \brief Reference initialized from a parenthesized initializer list. + FK_ParenthesizedListInitForReference, /// \brief Array must be initialized with an initializer list. FK_ArrayNeedsInitList, /// \brief Array must be initialized with an initializer list or a @@ -866,6 +868,8 @@ public: FK_ConversionFromPropertyFailed, /// \brief Too many initializers for scalar FK_TooManyInitsForScalar, + /// \brief Scalar initialized from a parenthesized initializer list. + FK_ParenthesizedListInitForScalar, /// \brief Reference initialization from an initializer list FK_ReferenceBindingToInitList, /// \brief Initialization of some unused destination type with an @@ -892,7 +896,7 @@ public: /// having its address taken. FK_AddressOfUnaddressableFunction, /// \brief List-copy-initialization chose an explicit constructor. - FK_ExplicitConstructor + FK_ExplicitConstructor, }; private: Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Thu Mar 23 20:14:25 2017 @@ -1822,7 +1822,6 @@ public: void AddInitializerToDecl(Decl *dcl, Expr *init, bool DirectInit); void ActOnUninitializedDecl(Decl *dcl); void ActOnInitializerError(Decl *Dcl); - bool canInitializeWithParenthesizedList(QualType TargetType); void ActOnPureSpecifier(Decl *D, SourceLocation PureSpecLoc); void ActOnCXXForRangeDecl(Decl *D); Modified: cfe/trunk/lib/Sema/SemaDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaDecl.cpp Thu Mar 23 20:14:25 2017 @@ -10102,18 +10102,6 @@ void Sema::AddInitializerToDecl(Decl *Re // Perform the initialization. ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init); if (!VDecl->isInvalidDecl()) { - // Handle errors like: int a({0}) - if (CXXDirectInit && CXXDirectInit->getNumExprs() == 1 && - !canInitializeWithParenthesizedList(VDecl->getType())) - if (auto IList = dyn_cast<InitListExpr>(CXXDirectInit->getExpr(0))) { - Diag(VDecl->getLocation(), diag::err_list_init_in_parens) - << VDecl->getType() << CXXDirectInit->getSourceRange() - << FixItHint::CreateRemoval(CXXDirectInit->getLocStart()) - << FixItHint::CreateRemoval(CXXDirectInit->getLocEnd()); - Init = IList; - CXXDirectInit = nullptr; - } - InitializedEntity Entity = InitializedEntity::InitializeVariable(VDecl); InitializationKind Kind = InitializationKind::CreateForInit( VDecl->getLocation(), DirectInit, Init); @@ -10413,18 +10401,6 @@ void Sema::ActOnInitializerError(Decl *D // though. } -/// Checks if an object of the given type can be initialized with parenthesized -/// init-list. -/// -/// \param TargetType Type of object being initialized. -/// -/// The function is used to detect wrong initializations, such as 'int({0})'. -/// -bool Sema::canInitializeWithParenthesizedList(QualType TargetType) { - return TargetType->isDependentType() || TargetType->isRecordType() || - TargetType->getContainedAutoType(); -} - void Sema::ActOnUninitializedDecl(Decl *RealDecl) { // If there is no declaration, there was an error parsing it. Just ignore it. if (!RealDecl) Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Thu Mar 23 20:14:25 2017 @@ -1228,17 +1228,6 @@ Sema::ActOnCXXTypeConstructExpr(ParsedTy if (!TInfo) TInfo = Context.getTrivialTypeSourceInfo(Ty, SourceLocation()); - // Handle errors like: int({0}) - if (exprs.size() == 1 && !canInitializeWithParenthesizedList(Ty) && - LParenLoc.isValid() && RParenLoc.isValid()) - if (auto IList = dyn_cast<InitListExpr>(exprs[0])) { - Diag(TInfo->getTypeLoc().getLocStart(), diag::err_list_init_in_parens) - << Ty << IList->getSourceRange() - << FixItHint::CreateRemoval(LParenLoc) - << FixItHint::CreateRemoval(RParenLoc); - LParenLoc = RParenLoc = SourceLocation(); - } - auto Result = BuildCXXTypeConstructExpr(TInfo, LParenLoc, exprs, RParenLoc); // Avoid creating a non-type-dependent expression that contains typos. // Non-type-dependent expressions are liable to be discarded without @@ -1295,46 +1284,51 @@ Sema::BuildCXXTypeConstructExpr(TypeSour } // 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) { + // If the expression list is a parenthesized 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 && + !isa<InitListExpr>(Exprs[0])) { Expr *Arg = Exprs[0]; return BuildCXXFunctionalCastExpr(TInfo, Ty, LParenLoc, Arg, RParenLoc); } - // C++14 [expr.type.conv]p2: The expression T(), where T is a - // simple-type-specifier or typename-specifier for a non-array complete - // object type or the (possibly cv-qualified) void type, creates a prvalue - // of the specified type, whose value is that produced by value-initializing - // an object of type T. + // For an expression of the form T(), T shall not be an array type. QualType ElemTy = Ty; if (Ty->isArrayType()) { if (!ListInitialization) - return ExprError(Diag(TyBeginLoc, - diag::err_value_init_for_array_type) << FullRange); + return ExprError(Diag(TyBeginLoc, diag::err_value_init_for_array_type) + << FullRange); ElemTy = Context.getBaseElementType(Ty); } - if (!ListInitialization && Ty->isFunctionType()) - return ExprError(Diag(TyBeginLoc, diag::err_value_init_for_function_type) - << FullRange); - + // There doesn't seem to be an explicit rule against this but sanity demands + // we only construct objects with object types. + if (Ty->isFunctionType()) + return ExprError(Diag(TyBeginLoc, diag::err_init_for_function_type) + << Ty << FullRange); + + // C++17 [expr.type.conv]p2: + // If the type is cv void and the initializer is (), the expression is a + // prvalue of the specified type that performs no initialization. if (!Ty->isVoidType() && RequireCompleteType(TyBeginLoc, ElemTy, diag::err_invalid_incomplete_type_use, FullRange)) return ExprError(); + // Otherwise, the expression is a prvalue of the specified type whose + // result object is direct-initialized (11.6) with the initializer. InitializationSequence InitSeq(*this, Entity, Kind, Exprs); ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Exprs); - if (Result.isInvalid() || !ListInitialization) + if (Result.isInvalid()) return Result; Expr *Inner = Result.get(); if (CXXBindTemporaryExpr *BTE = dyn_cast_or_null<CXXBindTemporaryExpr>(Inner)) Inner = BTE->getSubExpr(); - if (!isa<CXXTemporaryObjectExpr>(Inner)) { + if (!isa<CXXTemporaryObjectExpr>(Inner) && + !isa<CXXScalarValueInitExpr>(Inner)) { // If we created a CXXTemporaryObjectExpr, that node also represents the // functional cast. Otherwise, create an explicit cast to represent // the syntactic form of a functional-style cast that was used here. @@ -1590,20 +1584,8 @@ Sema::ActOnCXXNew(SourceLocation StartLo return ExprError(); SourceRange DirectInitRange; - if (ParenListExpr *List = dyn_cast_or_null<ParenListExpr>(Initializer)) { + if (ParenListExpr *List = dyn_cast_or_null<ParenListExpr>(Initializer)) DirectInitRange = List->getSourceRange(); - // Handle errors like: new int a({0}) - if (List->getNumExprs() == 1 && - !canInitializeWithParenthesizedList(AllocType)) - if (auto IList = dyn_cast<InitListExpr>(List->getExpr(0))) { - Diag(TInfo->getTypeLoc().getLocStart(), diag::err_list_init_in_parens) - << AllocType << List->getSourceRange() - << FixItHint::CreateRemoval(List->getLocStart()) - << FixItHint::CreateRemoval(List->getLocEnd()); - DirectInitRange = SourceRange(); - Initializer = IList; - } - } return BuildCXXNew(SourceRange(StartLoc, D.getLocEnd()), UseGlobal, PlacementLParen, Modified: cfe/trunk/lib/Sema/SemaInit.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaInit.cpp (original) +++ cfe/trunk/lib/Sema/SemaInit.cpp Thu Mar 23 20:14:25 2017 @@ -3112,6 +3112,7 @@ bool InitializationSequence::isAmbiguous switch (getFailureKind()) { case FK_TooManyInitsForReference: + case FK_ParenthesizedListInitForReference: case FK_ArrayNeedsInitList: case FK_ArrayNeedsInitListOrStringLiteral: case FK_ArrayNeedsInitListOrWideStringLiteral: @@ -3129,6 +3130,7 @@ bool InitializationSequence::isAmbiguous case FK_ConversionFailed: case FK_ConversionFromPropertyFailed: case FK_TooManyInitsForScalar: + case FK_ParenthesizedListInitForScalar: case FK_ReferenceBindingToInitList: case FK_InitListBadDestinationType: case FK_DefaultInitOfConst: @@ -5179,6 +5181,12 @@ void InitializationSequence::InitializeF // (Therefore, multiple arguments are not permitted.) if (Args.size() != 1) SetFailed(FK_TooManyInitsForReference); + // C++17 [dcl.init.ref]p5: + // A reference [...] is initialized by an expression [...] as follows: + // If the initializer is not an expression, presumably we should reject, + // but the standard fails to actually say so. + else if (isa<InitListExpr>(Args[0])) + SetFailed(FK_ParenthesizedListInitForReference); else TryReferenceInitialization(S, Entity, Kind, Args[0], *this); return; @@ -5344,11 +5352,16 @@ void InitializationSequence::InitializeF return; } + assert(Args.size() >= 1 && "Zero-argument case handled above"); + + // The remaining cases all need a source type. if (Args.size() > 1) { SetFailed(FK_TooManyInitsForScalar); return; + } else if (isa<InitListExpr>(Args[0])) { + SetFailed(FK_ParenthesizedListInitForScalar); + return; } - assert(Args.size() == 1 && "Zero-argument case handled above"); // - Otherwise, if the source type is a (possibly cv-qualified) class // type, conversion functions are considered. @@ -7389,6 +7402,10 @@ bool InitializationSequence::Diagnose(Se S.Diag(Kind.getLocation(), diag::err_reference_has_multiple_inits) << SourceRange(Args.front()->getLocStart(), Args.back()->getLocEnd()); break; + case FK_ParenthesizedListInitForReference: + S.Diag(Kind.getLocation(), diag::err_list_init_in_parens) + << 1 << Entity.getType() << Args[0]->getSourceRange(); + break; case FK_ArrayNeedsInitList: S.Diag(Kind.getLocation(), diag::err_array_init_not_init_list) << 0; @@ -7600,6 +7617,11 @@ bool InitializationSequence::Diagnose(Se break; } + case FK_ParenthesizedListInitForScalar: + S.Diag(Kind.getLocation(), diag::err_list_init_in_parens) + << 0 << Entity.getType() << Args[0]->getSourceRange(); + break; + case FK_ReferenceBindingToInitList: S.Diag(Kind.getLocation(), diag::err_reference_bind_init_list) << DestType.getNonReferenceType() << Args[0]->getSourceRange(); @@ -7782,6 +7804,10 @@ void InitializationSequence::dump(raw_os OS << "too many initializers for reference"; break; + case FK_ParenthesizedListInitForReference: + OS << "parenthesized list init for reference"; + break; + case FK_ArrayNeedsInitList: OS << "array requires initializer list"; break; @@ -7866,6 +7892,10 @@ void InitializationSequence::dump(raw_os OS << "too many initializers for scalar"; break; + case FK_ParenthesizedListInitForScalar: + OS << "parenthesized list init for reference"; + break; + case FK_ReferenceBindingToInitList: OS << "referencing binding to initializer list"; break; Modified: cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp (original) +++ cfe/trunk/test/SemaCXX/cxx0x-initializer-constructor.cpp Thu Mar 23 20:14:25 2017 @@ -173,8 +173,7 @@ namespace objects { // invalid H h1({1, 2}); // expected-error {{no matching constructor}} (void) new H({1, 2}); // expected-error {{no matching constructor}} - // FIXME: Bad diagnostic, mentions void type instead of init list. - (void) H({1, 2}); // expected-error {{no matching conversion}} + (void) H({1, 2}); // expected-error {{no matching constructor}} // valid (by copy constructor). H h2({1, nullptr}); Modified: cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp (original) +++ cfe/trunk/test/SemaCXX/cxx0x-initializer-references.cpp Thu Mar 23 20:14:25 2017 @@ -71,10 +71,22 @@ namespace reference { static_assert(sizeof(h({1, 2})) == sizeof(two), "bad overload resolution"); } + struct X {}; + void edge_cases() { - int const &b({0}); // expected-error {{list-initializer for non-class type 'const int &' must not be parenthesized}} - const int (&arr)[3] ({1, 2, 3}); // expected-error {{list-initializer for non-class type 'const int (&)[3]' must not be parenthesized}} + int const &b({0}); // expected-error {{cannot initialize reference type 'const int &' with a parenthesized initializer list}} + const int (&arr)[3] ({1, 2, 3}); // expected-error {{cannot initialize reference type 'const int (&)[3]' with a parenthesized initializer list}} + const X &x({}); // expected-error {{cannot initialize reference type 'const reference::X &' with a parenthesized initializer list}} + } + + template<typename T> void dependent_edge_cases() { + T b({}); // expected-error-re 3{{cannot initialize reference type {{.*}} with a parenthesized init}} + T({}); // expected-error-re 3{{cannot initialize reference type {{.*}} with a parenthesized init}} } + template void dependent_edge_cases<X>(); // ok + template void dependent_edge_cases<const int&>(); // expected-note {{instantiation of}} + template void dependent_edge_cases<const int(&)[1]>(); // expected-note {{instantiation of}} + template void dependent_edge_cases<const X&>(); // expected-note {{instantiation of}} } namespace PR12182 { Modified: cfe/trunk/test/SemaCXX/cxx0x-initializer-scalars.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx0x-initializer-scalars.cpp?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/cxx0x-initializer-scalars.cpp (original) +++ cfe/trunk/test/SemaCXX/cxx0x-initializer-scalars.cpp Thu Mar 23 20:14:25 2017 @@ -91,13 +91,13 @@ namespace integral { } void edge_cases() { - int a({0}); // expected-error {{list-initializer for non-class type 'int' must not be parenthesized}} - (void) int({0}); // expected-error {{list-initializer for non-class type 'int' must not be parenthesized}} - new int({0}); // expected-error {{list-initializer for non-class type 'int' must not be parenthesized}} + int a({0}); // expected-error {{cannot initialize non-class type 'int' with a parenthesized initializer list}} + (void) int({0}); // expected-error {{cannot initialize non-class type 'int' with a parenthesized initializer list}} + new int({0}); // expected-error {{cannot initialize non-class type 'int' with a parenthesized initializer list}} - int *b({0}); // expected-error {{list-initializer for non-class type 'int *' must not be parenthesized}} + int *b({0}); // expected-error {{cannot initialize non-class type 'int *' with a parenthesized initializer list}} typedef int *intptr; - int *c = intptr({0}); // expected-error {{list-initializer for non-class type 'intptr' (aka 'int *') must not be parenthesized}} + int *c = intptr({0}); // expected-error {{cannot initialize non-class type 'intptr' (aka 'int *') with a parenthesized initializer list}} } template<typename T> void dependent_edge_cases() { Modified: cfe/trunk/test/SemaCXX/type-convert-construct.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/type-convert-construct.cpp?rev=298676&r1=298675&r2=298676&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/type-convert-construct.cpp (original) +++ cfe/trunk/test/SemaCXX/type-convert-construct.cpp Thu Mar 23 20:14:25 2017 @@ -8,7 +8,15 @@ void f() { typedef int arr[]; int v3 = arr(); // expected-error {{array types cannot be value-initialized}} typedef void fn_ty(); - fn_ty(); // expected-error {{function types cannot be value-initialized}} + fn_ty(); // expected-error {{cannot create object of function type 'fn_ty'}} + fn_ty(0); // expected-error {{functional-style cast from 'int' to 'fn_ty'}} + fn_ty(0, 0); // expected-error {{cannot create object of function type 'fn_ty'}} +#if __cplusplus >= 201103L + fn_ty{}; // expected-error {{cannot create object of function type 'fn_ty'}} + fn_ty{0}; // expected-error {{cannot create object of function type 'fn_ty'}} + fn_ty{0, 0}; // expected-error {{cannot create object of function type 'fn_ty'}} + fn_ty({}); // expected-error {{cannot create object of function type 'fn_ty'}} +#endif int v4 = int(); int v5 = int; // expected-error {{expected '(' for function-style cast or type construction}} typedef int T; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits