r298676 - Fix handling of initialization from parenthesized initializer list.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 23 18:14:26 PDT 2017


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;




More information about the cfe-commits mailing list