]> granicus.if.org Git - clang/commitdiff
[c++20] P1331R2: Allow transient use of uninitialized objects in
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 18 Sep 2019 17:37:44 +0000 (17:37 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 18 Sep 2019 17:37:44 +0000 (17:37 +0000)
constant evaluation.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@372237 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
include/clang/AST/DeclCXX.h
include/clang/Basic/DiagnosticASTKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/DeclCXX.cpp
lib/AST/ExprConstant.cpp
lib/AST/Interp/State.h
lib/Sema/SemaDeclCXX.cpp
test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
test/CXX/drs/dr14xx.cpp
test/SemaCXX/constant-expression-cxx11.cpp
test/SemaCXX/constant-expression-cxx1y.cpp
test/SemaCXX/constant-expression-cxx2a.cpp
www/cxx_status.html

index f9668f1dce1f8a67e4fea2ce5e82743f4b1d3f62..49a0e6e98262eb7ad6fd23c7480a63e47e8ac166 100644 (file)
@@ -1352,7 +1352,8 @@ public:
   /// would be constexpr.
   bool defaultedDefaultConstructorIsConstexpr() const {
     return data().DefaultedDefaultConstructorIsConstexpr &&
-           (!isUnion() || hasInClassInitializer() || !hasVariantMembers());
+           (!isUnion() || hasInClassInitializer() || !hasVariantMembers() ||
+            getASTContext().getLangOpts().CPlusPlus2a);
   }
 
   /// Determine whether this class has a constexpr default constructor.
index 7732be56595a40e3d14e1525fdcfe77cf6504bc8..98c1269e0333635e5291b2c55ee0607611389c2c 100644 (file)
@@ -37,7 +37,7 @@ def note_constexpr_virtual_call : Note<
 def note_constexpr_pure_virtual_call : Note<
   "pure virtual function %q0 called">;
 def note_constexpr_polymorphic_unknown_dynamic_type : Note<
-  "%select{||||virtual function called on|dynamic_cast applied to|"
+  "%select{|||||virtual function called on|dynamic_cast applied to|"
   "typeid applied to}0 object '%1' whose dynamic type is not constant">;
 def note_constexpr_dynamic_cast_to_reference_failed : Note<
   "reference dynamic_cast failed: %select{"
@@ -113,12 +113,12 @@ def note_constexpr_this : Note<
   "%select{|implicit }0use of 'this' pointer is only allowed within the "
   "evaluation of a call to a 'constexpr' member function">;
 def note_constexpr_lifetime_ended : Note<
-  "%select{read of|assignment to|increment of|decrement of|member call on|"
-  "dynamic_cast of|typeid applied to}0 "
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "member call on|dynamic_cast of|typeid applied to}0 "
   "%select{temporary|variable}1 whose lifetime has ended">;
 def note_constexpr_access_uninit : Note<
-  "%select{read of|assignment to|increment of|decrement of|member call on|"
-  "dynamic_cast of|typeid applied to}0 "
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "member call on|dynamic_cast of|typeid applied to}0 "
   "%select{object outside its lifetime|uninitialized object}1 "
   "is not allowed in a constant expression">;
 def note_constexpr_use_uninit_reference : Note<
@@ -128,10 +128,12 @@ def note_constexpr_modify_const_type : Note<
   "modification of object of const-qualified type %0 is not allowed "
   "in a constant expression">;
 def note_constexpr_access_volatile_type : Note<
-  "%select{read of|assignment to|increment of|decrement of|<ERROR>|<ERROR>}0 "
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "<ERROR>|<ERROR>}0 "
   "volatile-qualified type %1 is not allowed in a constant expression">;
 def note_constexpr_access_volatile_obj : Note<
-  "%select{read of|assignment to|increment of|decrement of|<ERROR>|<ERROR>}0 "
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "<ERROR>|<ERROR>}0 "
   "volatile %select{temporary|object %2|member %2}1 is not allowed in "
   "a constant expression">;
 def note_constexpr_volatile_here : Note<
@@ -145,31 +147,33 @@ def note_constexpr_ltor_non_constexpr : Note<
 def note_constexpr_ltor_incomplete_type : Note<
   "read of incomplete type %0 is not allowed in a constant expression">;
 def note_constexpr_access_null : Note<
-  "%select{read of|assignment to|increment of|decrement of|member call on|"
-  "dynamic_cast of|typeid applied to}0 "
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "member call on|dynamic_cast of|typeid applied to}0 "
   "dereferenced null pointer is not allowed in a constant expression">;
 def note_constexpr_access_past_end : Note<
-  "%select{read of|assignment to|increment of|decrement of|member call on|"
-  "dynamic_cast of|typeid applied to}0 "
-  "dereferenced one-past-the-end pointer is not allowed in a constant expression">;
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "member call on|dynamic_cast of|typeid applied to}0 "
+  "dereferenced one-past-the-end pointer is not allowed "
+  "in a constant expression">;
 def note_constexpr_access_unsized_array : Note<
-  "%select{read of|assignment to|increment of|decrement of|member call on|"
-  "dynamic_cast of|typeid applied to}0 "
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "member call on|dynamic_cast of|typeid applied to}0 "
   "element of array without known bound "
   "is not allowed in a constant expression">;
 def note_constexpr_access_inactive_union_member : Note<
-  "%select{read of|assignment to|increment of|decrement of|member call on|"
-  "dynamic_cast of|typeid applied to}0 "
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "member call on|dynamic_cast of|typeid applied to}0 "
   "member %1 of union with %select{active member %3|no active member}2 "
   "is not allowed in a constant expression">;
 def note_constexpr_access_static_temporary : Note<
-  "%select{read of|assignment to|increment of|decrement of|member call on|"
-  "dynamic_cast of|typeid applied to}0 temporary "
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "member call on|dynamic_cast of|typeid applied to}0 temporary "
   "is not allowed in a constant expression outside the expression that "
   "created the temporary">;
 def note_constexpr_access_unreadable_object : Note<
-  "%select{read of|assignment to|increment of|decrement of|member call on|"
-  "dynamic_cast of|typeid applied to}0 object '%1' whose value is not known">;
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "member call on|dynamic_cast of|typeid applied to}0 object '%1' "
+  "whose value is not known">;
 def note_constexpr_modify_global : Note<
   "a constant expression cannot modify an object that is visible outside "
   "that expression">;
index 35cca1303bccf8fc943d82e614477a7697676cb5..b89bd367579b01cf8de82e93707d1f1aedb632bb 100644 (file)
@@ -2438,9 +2438,13 @@ def err_constexpr_local_var_static : Error<
 def err_constexpr_local_var_non_literal_type : Error<
   "variable of non-literal type %1 cannot be defined in a constexpr "
   "%select{function|constructor}0">;
-def err_constexpr_local_var_no_init : Error<
-  "variables defined in a constexpr %select{function|constructor}0 must be "
-  "initialized">;
+def ext_constexpr_local_var_no_init : ExtWarn<
+  "uninitialized variable in a constexpr %select{function|constructor}0 "
+  "is a C++20 extension">, InGroup<CXX2a>;
+def warn_cxx17_compat_constexpr_local_var_no_init : Warning<
+  "uninitialized variable in a constexpr %select{function|constructor}0 "
+  "is incompatible with C++ standards before C++20">,
+  InGroup<CXXPre2aCompat>, DefaultIgnore;
 def ext_constexpr_function_never_constant_expr : ExtWarn<
   "constexpr %select{function|constructor}0 never produces a "
   "constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
@@ -2468,7 +2472,7 @@ def note_constexpr_body_previous_return : Note<
 def err_constexpr_function_try_block : Error<
   "function try block not allowed in constexpr %select{function|constructor}0">;
 
-// c++2a function try blocks in constexpr
+// C++2a function try blocks in constexpr
 def ext_constexpr_function_try_block_cxx2a : ExtWarn<
   "function try block in constexpr %select{function|constructor}0 is "
   "a C++2a extension">, InGroup<CXX2a>;
@@ -2477,10 +2481,20 @@ def warn_cxx17_compat_constexpr_function_try_block : Warning<
   "incompatible with C++ standards before C++2a">,
   InGroup<CXXPre2aCompat>, DefaultIgnore;
 
-def err_constexpr_union_ctor_no_init : Error<
-  "constexpr union constructor does not initialize any member">;
-def err_constexpr_ctor_missing_init : Error<
-  "constexpr constructor must initialize all members">;
+def ext_constexpr_union_ctor_no_init : ExtWarn<
+  "constexpr union constructor that does not initialize any member "
+  "is a C++20 extension">, InGroup<CXX2a>;
+def warn_cxx17_compat_constexpr_union_ctor_no_init : Warning<
+  "constexpr union constructor that does not initialize any member "
+  "is incompatible with C++ standards before C++20">,
+  InGroup<CXXPre2aCompat>, DefaultIgnore;
+def ext_constexpr_ctor_missing_init : ExtWarn<
+  "constexpr constructor that does not initialize all members "
+  "is a C++20 extension">, InGroup<CXX2a>;
+def warn_cxx17_compat_constexpr_ctor_missing_init : Warning<
+  "constexpr constructor that does not initialize all members "
+  "is incompatible with C++ standards before C++20">,
+  InGroup<CXXPre2aCompat>, DefaultIgnore;
 def note_constexpr_ctor_missing_init : Note<
   "member not initialized by constructor">;
 def note_non_literal_no_constexpr_ctors : Note<
index 4939c37edc7e879c49c54631d558f1b865802851..bcc22384ed90fdb131381c33e47b711aef5e449e 100644 (file)
@@ -1263,7 +1263,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
     } else {
       // Base element type of field is a non-class type.
       if (!T->isLiteralType(Context) ||
-          (!Field->hasInClassInitializer() && !isUnion()))
+          (!Field->hasInClassInitializer() && !isUnion() &&
+           !Context.getLangOpts().CPlusPlus2a))
         data().DefaultedDefaultConstructorIsConstexpr = false;
 
       // C++11 [class.copy]p23:
index a4c247370ee1b2a28581925bdf0f5d562f51d88d..2d538eda8128e5f353c3a74e832d992cd531efdb 100644 (file)
@@ -1192,9 +1192,14 @@ APValue &CallStackFrame::createTemporary(const void *Key,
   return Result;
 }
 
+static bool isRead(AccessKinds AK) {
+  return AK == AK_Read || AK == AK_ReadObjectRepresentation;
+}
+
 static bool isModification(AccessKinds AK) {
   switch (AK) {
   case AK_Read:
+  case AK_ReadObjectRepresentation:
   case AK_MemberCall:
   case AK_DynamicCast:
   case AK_TypeId:
@@ -1209,7 +1214,7 @@ static bool isModification(AccessKinds AK) {
 
 /// Is this an access per the C++ definition?
 static bool isFormalAccess(AccessKinds AK) {
-  return AK == AK_Read || isModification(AK);
+  return isRead(AK) || isModification(AK);
 }
 
 namespace {
@@ -1858,14 +1863,16 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E,
   return false;
 }
 
-/// Check that this core constant expression value is a valid value for a
-/// constant expression. If not, report an appropriate diagnostic. Does not
-/// check that the expression is of literal type.
+enum class CheckEvaluationResultKind {
+  ConstantExpression,
+  FullyInitialized,
+};
+
 static bool
-CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
-                        const APValue &Value,
-                        Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen,
-                        SourceLocation SubobjectLoc = SourceLocation()) {
+CheckEvaluationResult(CheckEvaluationResultKind CERK, EvalInfo &Info,
+                      SourceLocation DiagLoc, QualType Type,
+                      const APValue &Value, Expr::ConstExprUsage Usage,
+                      SourceLocation SubobjectLoc = SourceLocation()) {
   if (!Value.hasValue()) {
     Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized)
       << true << Type;
@@ -1885,30 +1892,29 @@ CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
   if (Value.isArray()) {
     QualType EltTy = Type->castAsArrayTypeUnsafe()->getElementType();
     for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) {
-      if (!CheckConstantExpression(Info, DiagLoc, EltTy,
-                                   Value.getArrayInitializedElt(I), Usage,
-                                   SubobjectLoc))
+      if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
+                                 Value.getArrayInitializedElt(I), Usage,
+                                 SubobjectLoc))
         return false;
     }
     if (!Value.hasArrayFiller())
       return true;
-    return CheckConstantExpression(Info, DiagLoc, EltTy, Value.getArrayFiller(),
-                                   Usage, SubobjectLoc);
+    return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
+                                 Value.getArrayFiller(), Usage, SubobjectLoc);
   }
   if (Value.isUnion() && Value.getUnionField()) {
-    return CheckConstantExpression(Info, DiagLoc,
-                                   Value.getUnionField()->getType(),
-                                   Value.getUnionValue(), Usage,
-                                   Value.getUnionField()->getLocation());
+    return CheckEvaluationResult(
+        CERK, Info, DiagLoc, Value.getUnionField()->getType(),
+        Value.getUnionValue(), Usage, Value.getUnionField()->getLocation());
   }
   if (Value.isStruct()) {
     RecordDecl *RD = Type->castAs<RecordType>()->getDecl();
     if (const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD)) {
       unsigned BaseIndex = 0;
       for (const CXXBaseSpecifier &BS : CD->bases()) {
-        if (!CheckConstantExpression(Info, DiagLoc, BS.getType(),
-                                     Value.getStructBase(BaseIndex), Usage,
-                                     BS.getBeginLoc()))
+        if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(),
+                                   Value.getStructBase(BaseIndex), Usage,
+                                   BS.getBeginLoc()))
           return false;
         ++BaseIndex;
       }
@@ -1917,26 +1923,48 @@ CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
       if (I->isUnnamedBitfield())
         continue;
 
-      if (!CheckConstantExpression(Info, DiagLoc, I->getType(),
-                                   Value.getStructField(I->getFieldIndex()),
-                                   Usage, I->getLocation()))
+      if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(),
+                                 Value.getStructField(I->getFieldIndex()),
+                                 Usage, I->getLocation()))
         return false;
     }
   }
 
-  if (Value.isLValue()) {
+  if (Value.isLValue() &&
+      CERK == CheckEvaluationResultKind::ConstantExpression) {
     LValue LVal;
     LVal.setFrom(Info.Ctx, Value);
     return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage);
   }
 
-  if (Value.isMemberPointer())
+  if (Value.isMemberPointer() &&
+      CERK == CheckEvaluationResultKind::ConstantExpression)
     return CheckMemberPointerConstantExpression(Info, DiagLoc, Type, Value, Usage);
 
   // Everything else is fine.
   return true;
 }
 
+/// Check that this core constant expression value is a valid value for a
+/// constant expression. If not, report an appropriate diagnostic. Does not
+/// check that the expression is of literal type.
+static bool
+CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
+                        const APValue &Value,
+                        Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen) {
+  return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression,
+                               Info, DiagLoc, Type, Value, Usage);
+}
+
+/// Check that this evaluated value is fully-initialized and can be loaded by
+/// an lvalue-to-rvalue conversion.
+static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc,
+                                  QualType Type, const APValue &Value) {
+  return CheckEvaluationResult(CheckEvaluationResultKind::FullyInitialized,
+                               Info, DiagLoc, Type, Value,
+                               Expr::EvaluateForCodeGen);
+}
+
 static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) {
   // A null base expression indicates a null pointer.  These are always
   // evaluatable, and they are false unless the offset is zero.
@@ -2821,7 +2849,9 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
   // Walk the designator's path to find the subobject.
   for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
     // Reading an indeterminate value is undefined, but assigning over one is OK.
-    if (O->isAbsent() || (O->isIndeterminate() && handler.AccessKind != AK_Assign)) {
+    if (O->isAbsent() ||
+        (O->isIndeterminate() && handler.AccessKind != AK_Assign &&
+         handler.AccessKind != AK_ReadObjectRepresentation)) {
       if (!Info.checkingPotentialConstantExpression())
         Info.FFDiag(E, diag::note_constexpr_access_uninit)
             << handler.AccessKind << O->isIndeterminate();
@@ -2876,7 +2906,7 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
       // things we need to check: if there are any mutable subobjects, we
       // cannot perform this read. (This only happens when performing a trivial
       // copy or assignment.)
-      if (ObjType->isRecordType() && handler.AccessKind == AK_Read &&
+      if (ObjType->isRecordType() && isRead(handler.AccessKind) &&
           !Obj.mayReadMutableMembers(Info) &&
           diagnoseUnreadableFields(Info, E, ObjType))
         return handler.failed();
@@ -2916,7 +2946,7 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
 
       if (O->getArrayInitializedElts() > Index)
         O = &O->getArrayInitializedElt(Index);
-      else if (handler.AccessKind != AK_Read) {
+      else if (!isRead(handler.AccessKind)) {
         expandArray(*O, Index);
         O = &O->getArrayInitializedElt(Index);
       } else
@@ -2946,7 +2976,7 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
                                    : O->getComplexFloatReal(), ObjType);
       }
     } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
-      if (Field->isMutable() && handler.AccessKind == AK_Read &&
+      if (Field->isMutable() && isRead(handler.AccessKind) &&
           !Obj.mayReadMutableMembers(Info)) {
         Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1)
           << Field;
@@ -2986,15 +3016,17 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
 namespace {
 struct ExtractSubobjectHandler {
   EvalInfo &Info;
+  const Expr *E;
   APValue &Result;
-
-  static const AccessKinds AccessKind = AK_Read;
+  const AccessKinds AccessKind;
 
   typedef bool result_type;
   bool failed() { return false; }
   bool found(APValue &Subobj, QualType SubobjType) {
     Result = Subobj;
-    return true;
+    if (AccessKind == AK_ReadObjectRepresentation)
+      return true;
+    return CheckFullyInitialized(Info, E->getExprLoc(), SubobjType, Result);
   }
   bool found(APSInt &Value, QualType SubobjType) {
     Result = APValue(Value);
@@ -3007,14 +3039,13 @@ struct ExtractSubobjectHandler {
 };
 } // end anonymous namespace
 
-const AccessKinds ExtractSubobjectHandler::AccessKind;
-
 /// Extract the designated sub-object of an rvalue.
 static bool extractSubobject(EvalInfo &Info, const Expr *E,
                              const CompleteObject &Obj,
-                             const SubobjectDesignator &Sub,
-                             APValue &Result) {
-  ExtractSubobjectHandler Handler = { Info, Result };
+                             const SubobjectDesignator &Sub, APValue &Result,
+                             AccessKinds AK = AK_Read) {
+  assert(AK == AK_Read || AK == AK_ReadObjectRepresentation);
+  ExtractSubobjectHandler Handler = {Info, E, Result, AK};
   return findSubobject(Info, E, Obj, Sub, Handler);
 }
 
@@ -3340,15 +3371,22 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
 ///               case of a non-class type).
 /// \param LVal - The glvalue on which we are attempting to perform this action.
 /// \param RVal - The produced value will be placed here.
-static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
-                                           QualType Type,
-                                           const LValue &LVal, APValue &RVal) {
+/// \param WantObjectRepresentation - If true, we're looking for the object
+///               representation rather than the value, and in particular,
+///               there is no requirement that the result be fully initialized.
+static bool
+handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type,
+                               const LValue &LVal, APValue &RVal,
+                               bool WantObjectRepresentation = false) {
   if (LVal.Designator.Invalid)
     return false;
 
   // Check for special cases where there is no existing APValue to look at.
   const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
 
+  AccessKinds AK =
+      WantObjectRepresentation ? AK_ReadObjectRepresentation : AK_Read;
+
   if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) {
     if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(Base)) {
       // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the
@@ -3362,7 +3400,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
       if (!Evaluate(Lit, Info, CLE->getInitializer()))
         return false;
       CompleteObject LitObj(LVal.Base, &Lit, Base->getType());
-      return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal);
+      return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal, AK);
     } else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) {
       // Special-case character extraction so we don't have to construct an
       // APValue for the whole string.
@@ -3377,7 +3415,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
       }
       if (LVal.Designator.isOnePastTheEnd()) {
         if (Info.getLangOpts().CPlusPlus11)
-          Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK_Read;
+          Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK;
         else
           Info.FFDiag(Conv);
         return false;
@@ -3388,8 +3426,8 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
     }
   }
 
-  CompleteObject Obj = findCompleteObject(Info, Conv, AK_Read, LVal, Type);
-  return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal);
+  CompleteObject Obj = findCompleteObject(Info, Conv, AK, LVal, Type);
+  return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal, AK);
 }
 
 /// Perform an assignment of Val to LVal. Takes ownership of Val.
@@ -3843,6 +3881,40 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E,
   return CastToDerivedClass(Info, E, Result, TargetType, NewEntriesSize);
 }
 
+/// Get the value to use for a default-initialized object of type T.
+static APValue getDefaultInitValue(QualType T) {
+  if (auto *RD = T->getAsCXXRecordDecl()) {
+    if (RD->isUnion())
+      return APValue((const FieldDecl*)nullptr);
+
+    APValue Struct(APValue::UninitStruct(), RD->getNumBases(),
+                   std::distance(RD->field_begin(), RD->field_end()));
+
+    unsigned Index = 0;
+    for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(),
+           End = RD->bases_end(); I != End; ++I, ++Index)
+      Struct.getStructBase(Index) = getDefaultInitValue(I->getType());
+
+    for (const auto *I : RD->fields()) {
+      if (I->isUnnamedBitfield())
+        continue;
+      Struct.getStructField(I->getFieldIndex()) =
+          getDefaultInitValue(I->getType());
+    }
+    return Struct;
+  }
+
+  if (auto *AT =
+          dyn_cast_or_null<ConstantArrayType>(T->getAsArrayTypeUnsafe())) {
+    APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue());
+    if (Array.hasArrayFiller())
+      Array.getArrayFiller() = getDefaultInitValue(AT->getElementType());
+    return Array;
+  }
+
+  return APValue::IndeterminateValue();
+}
+
 namespace {
 enum EvalStmtResult {
   /// Evaluation failed.
@@ -3870,10 +3942,8 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
 
   const Expr *InitE = VD->getInit();
   if (!InitE) {
-    Info.FFDiag(VD->getBeginLoc(), diag::note_constexpr_uninitialized)
-        << false << VD->getType();
-    Val = APValue();
-    return false;
+    Val = getDefaultInitValue(VD->getType());
+    return true;
   }
 
   if (InitE->isValueDependent())
@@ -4089,9 +4159,23 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
       break;
     }
 
-    case Stmt::DeclStmtClass:
-      // FIXME: If the variable has initialization that can't be jumped over,
-      // bail out of any immediately-surrounding compound-statement too.
+    case Stmt::DeclStmtClass: {
+      // Start the lifetime of any uninitialized variables we encounter. They
+      // might be used by the selected branch of the switch.
+      const DeclStmt *DS = cast<DeclStmt>(S);
+      for (const auto *D : DS->decls()) {
+        if (const auto *VD = dyn_cast<VarDecl>(D)) {
+          if (VD->hasLocalStorage() && !VD->getInit())
+            if (!EvaluateVarDecl(Info, VD))
+              return ESR_Failed;
+          // FIXME: If the variable has initialization that can't be jumped
+          // over, bail out of any immediately-surrounding compound-statement
+          // too. There can't be any case labels here.
+        }
+      }
+      return ESR_CaseNotFound;
+    }
+
     default:
       return ESR_CaseNotFound;
     }
@@ -4116,12 +4200,10 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
 
   case Stmt::DeclStmtClass: {
     const DeclStmt *DS = cast<DeclStmt>(S);
-    for (const auto *DclIt : DS->decls()) {
+    for (const auto *D : DS->decls()) {
       // Each declaration initialization is its own full-expression.
-      // FIXME: This isn't quite right; if we're performing aggregate
-      // initialization, each braced subexpression is its own full-expression.
       FullExpressionRAII Scope(Info);
-      if (!EvaluateDecl(Info, DclIt) && !Info.noteFailure())
+      if (!EvaluateDecl(Info, D) && !Info.noteFailure())
         return ESR_Failed;
     }
     return ESR_Succeeded;
@@ -4743,39 +4825,6 @@ struct StartLifetimeOfUnionMemberHandler {
 
   static const AccessKinds AccessKind = AK_Assign;
 
-  APValue getDefaultInitValue(QualType SubobjType) {
-    if (auto *RD = SubobjType->getAsCXXRecordDecl()) {
-      if (RD->isUnion())
-        return APValue((const FieldDecl*)nullptr);
-
-      APValue Struct(APValue::UninitStruct(), RD->getNumBases(),
-                     std::distance(RD->field_begin(), RD->field_end()));
-
-      unsigned Index = 0;
-      for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(),
-             End = RD->bases_end(); I != End; ++I, ++Index)
-        Struct.getStructBase(Index) = getDefaultInitValue(I->getType());
-
-      for (const auto *I : RD->fields()) {
-        if (I->isUnnamedBitfield())
-          continue;
-        Struct.getStructField(I->getFieldIndex()) =
-            getDefaultInitValue(I->getType());
-      }
-      return Struct;
-    }
-
-    if (auto *AT = dyn_cast_or_null<ConstantArrayType>(
-            SubobjType->getAsArrayTypeUnsafe())) {
-      APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue());
-      if (Array.hasArrayFiller())
-        Array.getArrayFiller() = getDefaultInitValue(AT->getElementType());
-      return Array;
-    }
-
-    return APValue::IndeterminateValue();
-  }
-
   typedef bool result_type;
   bool failed() { return false; }
   bool found(APValue &Subobj, QualType SubobjType) {
@@ -4981,8 +5030,8 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
     LValue RHS;
     RHS.setFrom(Info.Ctx, ArgValues[0]);
     APValue RHSValue;
-    if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
-                                        RHS, RHSValue))
+    if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), RHS,
+                                        RHSValue, MD->getParent()->isUnion()))
       return false;
     if (Info.getLangOpts().CPlusPlus2a && MD->isTrivial() &&
         !HandleUnionActiveMemberChange(Info, Args[0], *This))
@@ -5066,7 +5115,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
     RHS.setFrom(Info.Ctx, ArgValues[0]);
     return handleLValueToRValueConversion(
         Info, E, Definition->getParamDecl(0)->getType().getNonReferenceType(),
-        RHS, Result);
+        RHS, Result, Definition->getParent()->isUnion());
   }
 
   // Reserve space for the struct members.
@@ -5085,6 +5134,25 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
 #ifndef NDEBUG
   CXXRecordDecl::base_class_const_iterator BaseIt = RD->bases_begin();
 #endif
+  CXXRecordDecl::field_iterator FieldIt = RD->field_begin();
+  auto SkipToField = [&](FieldDecl *FD, bool Indirect) {
+    // We might be initializing the same field again if this is an indirect
+    // field initialization.
+    if (FieldIt == RD->field_end() ||
+        FieldIt->getFieldIndex() > FD->getFieldIndex()) {
+      assert(Indirect && "fields out of order?");
+      return;
+    }
+
+    // Default-initialize any fields with no explicit initializer.
+    for (; !declaresSameEntity(*FieldIt, FD); ++FieldIt) {
+      assert(FieldIt != RD->field_end() && "missing field?");
+      if (!FieldIt->isUnnamedBitfield())
+        Result.getStructField(FieldIt->getFieldIndex()) =
+            getDefaultInitValue(FieldIt->getType());
+    }
+    ++FieldIt;
+  };
   for (const auto *I : Definition->inits()) {
     LValue Subobject = This;
     LValue SubobjectParent = This;
@@ -5113,6 +5181,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
         Result = APValue(FD);
         Value = &Result.getUnionValue();
       } else {
+        SkipToField(FD, false);
         Value = &Result.getStructField(FD->getFieldIndex());
       }
     } else if (IndirectFieldDecl *IFD = I->getIndirectMember()) {
@@ -5132,8 +5201,10 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
           if (CD->isUnion())
             *Value = APValue(FD);
           else
-            *Value = APValue(APValue::UninitStruct(), CD->getNumBases(),
-                             std::distance(CD->field_begin(), CD->field_end()));
+            // FIXME: This immediately starts the lifetime of all members of an
+            // anonymous struct. It would be preferable to strictly start member
+            // lifetime in initialization order.
+            *Value = getDefaultInitValue(Info.Ctx.getRecordType(CD));
         }
         // Store Subobject as its parent before updating it for the last element
         // in the chain.
@@ -5143,8 +5214,11 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
           return false;
         if (CD->isUnion())
           Value = &Value->getUnionValue();
-        else
+        else {
+          if (C == IndirectFieldChain.front() && !RD->isUnion())
+            SkipToField(FD, true);
           Value = &Value->getStructField(FD->getFieldIndex());
+        }
       }
     } else {
       llvm_unreachable("unknown base initializer kind");
@@ -5173,6 +5247,15 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
       EvalObj.finishedConstructingBases();
   }
 
+  // Default-initialize any remaining fields.
+  if (!RD->isUnion()) {
+    for (; FieldIt != RD->field_end(); ++FieldIt) {
+      if (!FieldIt->isUnnamedBitfield())
+        Result.getStructField(FieldIt->getFieldIndex()) =
+            getDefaultInitValue(FieldIt->getType());
+    }
+  }
+
   return Success &&
          EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
 }
@@ -5658,9 +5741,9 @@ static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue,
   LValue SourceLValue;
   APValue SourceRValue;
   SourceLValue.setFrom(Info.Ctx, SourceValue);
-  if (!handleLValueToRValueConversion(Info, BCE,
-                                      BCE->getSubExpr()->getType().withConst(),
-                                      SourceLValue, SourceRValue))
+  if (!handleLValueToRValueConversion(
+          Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue,
+          SourceRValue, /*WantObjectRepresentation=*/true))
     return false;
 
   // Read out SourceValue into a char buffer.
@@ -7455,6 +7538,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
 
     while (true) {
       APValue Val;
+      // FIXME: Set WantObjectRepresentation to true if we're copying a
+      // char-like type?
       if (!handleLValueToRValueConversion(Info, E, T, Src, Val) ||
           !handleAssignment(Info, E, Dest, T, Val))
         return false;
@@ -7830,15 +7915,11 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E,
     if (Result.hasValue())
       return true;
 
-    // We can get here in two different ways:
-    //  1) We're performing value-initialization, and should zero-initialize
-    //     the object, or
-    //  2) We're performing default-initialization of an object with a trivial
-    //     constexpr default constructor, in which case we should start the
-    //     lifetimes of all the base subobjects (there can be no data member
-    //     subobjects in this case) per [basic.life]p1.
-    // Either way, ZeroInitialization is appropriate.
-    return ZeroInitialization(E, T);
+    if (ZeroInit)
+      return ZeroInitialization(E, T);
+
+    Result = getDefaultInitValue(T);
+    return true;
   }
 
   const FunctionDecl *Definition = nullptr;
index 49af3d18c9bf56b8d6a7dde7c75d7578bbd47ef5..da19dd382045babb61289ac371359fce84f0a310 100644 (file)
@@ -25,6 +25,7 @@ namespace clang {
 /// same set of semantic restrictions.
 enum AccessKinds {
   AK_Read,
+  AK_ReadObjectRepresentation,
   AK_Assign,
   AK_Increment,
   AK_Decrement,
index 1bedecd7847a152f80c4864fb9f3fb34636e0089..f7c2695d57913764921fc3b73571064c580b993b 100644 (file)
@@ -1778,7 +1778,8 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
     case Decl::Decomposition: {
       // C++1y [dcl.constexpr]p3 allows anything except:
       //   a definition of a variable of non-literal type or of static or
-      //   thread storage duration or for which no initialization is performed.
+      //   thread storage duration or [before C++2a] for which no
+      //   initialization is performed.
       const auto *VD = cast<VarDecl>(DclIt);
       if (VD->isThisDeclarationADefinition()) {
         if (VD->isStaticLocal()) {
@@ -1797,11 +1798,16 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
         if (!VD->getType()->isDependentType() &&
             !VD->hasInit() && !VD->isCXXForRangeDecl()) {
           if (Kind == Sema::CheckConstexprKind::Diagnose) {
-            SemaRef.Diag(VD->getLocation(),
-                         diag::err_constexpr_local_var_no_init)
-              << isa<CXXConstructorDecl>(Dcl);
+            SemaRef.Diag(
+                VD->getLocation(),
+                SemaRef.getLangOpts().CPlusPlus2a
+                    ? diag::warn_cxx17_compat_constexpr_local_var_no_init
+                    : diag::ext_constexpr_local_var_no_init)
+                << isa<CXXConstructorDecl>(Dcl);
+          } else if (!SemaRef.getLangOpts().CPlusPlus2a) {
+            return false;
           }
-          return false;
+          continue;
         }
       }
       if (Kind == Sema::CheckConstexprKind::Diagnose) {
@@ -1855,6 +1861,11 @@ static bool CheckConstexprCtorInitializer(Sema &SemaRef,
                                           llvm::SmallSet<Decl*, 16> &Inits,
                                           bool &Diagnosed,
                                           Sema::CheckConstexprKind Kind) {
+  // In C++20 onwards, there's nothing to check for validity.
+  if (Kind == Sema::CheckConstexprKind::CheckValid &&
+      SemaRef.getLangOpts().CPlusPlus2a)
+    return true;
+
   if (Field->isInvalidDecl())
     return true;
 
@@ -1873,12 +1884,15 @@ static bool CheckConstexprCtorInitializer(Sema &SemaRef,
   if (!Inits.count(Field)) {
     if (Kind == Sema::CheckConstexprKind::Diagnose) {
       if (!Diagnosed) {
-        SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init);
+        SemaRef.Diag(Dcl->getLocation(),
+                     SemaRef.getLangOpts().CPlusPlus2a
+                         ? diag::warn_cxx17_compat_constexpr_ctor_missing_init
+                         : diag::ext_constexpr_ctor_missing_init);
         Diagnosed = true;
       }
       SemaRef.Diag(Field->getLocation(),
                    diag::note_constexpr_ctor_missing_init);
-    } else {
+    } else if (!SemaRef.getLangOpts().CPlusPlus2a) {
       return false;
     }
   } else if (Field->isAnonymousStructOrUnion()) {
@@ -2121,10 +2135,15 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
     if (RD->isUnion()) {
       if (Constructor->getNumCtorInitializers() == 0 &&
           RD->hasVariantMembers()) {
-        if (Kind == Sema::CheckConstexprKind::Diagnose)
-          SemaRef.Diag(Dcl->getLocation(),
-                       diag::err_constexpr_union_ctor_no_init);
-        return false;
+        if (Kind == Sema::CheckConstexprKind::Diagnose) {
+          SemaRef.Diag(
+              Dcl->getLocation(),
+              SemaRef.getLangOpts().CPlusPlus2a
+                  ? diag::warn_cxx17_compat_constexpr_union_ctor_no_init
+                  : diag::ext_constexpr_union_ctor_no_init);
+        } else if (!SemaRef.getLangOpts().CPlusPlus2a) {
+          return false;
+        }
       }
     } else if (!Constructor->isDependentContext() &&
                !Constructor->isDelegatingConstructor()) {
index f72984a6a12077857fa0e8650d1c0f9c707f4bcc..68196bc1c8d785d4a4eab4415f2ccf17dcde6248 100644 (file)
@@ -183,7 +183,10 @@ constexpr int DisallowedStmtsCXX1Y_6() {
 }
 constexpr int DisallowedStmtsCXX1Y_7() {
   //  - a definition of a variable for which no initialization is performed
-  int n; // expected-error {{variables defined in a constexpr function must be initialized}}
+  int n;
+#ifndef CXX2A
+  // expected-error@-2 {{uninitialized variable in a constexpr function}}
+#endif
   return 0;
 }
 
@@ -315,7 +318,10 @@ namespace std_example {
     return value;
   }
   constexpr int uninit() {
-    int a; // expected-error {{must be initialized}}
+    int a;
+#ifndef CXX2A
+    // expected-error@-2 {{uninitialized}}
+#endif
     return a;
   }
   constexpr int prev(int x) {
index 17753604b8a79882f3d8a7f45fad90a04ae1f9a6..39088042251f076c95e801c2c916edee2f6a0ce0 100644 (file)
@@ -136,29 +136,33 @@ struct V {
 
 // - every non-static data member and base class sub-object shall be initialized
 struct W {
-  int n; // expected-note {{member not initialized by constructor}}
-  constexpr W() {} // expected-error {{constexpr constructor must initialize all members}}
+  int n;
+  constexpr W() {}
+#ifndef CXX2A
+  // expected-error@-2 {{constexpr constructor that does not initialize all members}}
+  // expected-note@-4 {{member not initialized by constructor}}
+#endif
 };
 struct AnonMembers {
-  int a; // expected-note {{member not initialized by constructor}}
-  union { // expected-note 2{{member not initialized by constructor}}
+  int a; // expected-note 0-1{{member not initialized by constructor}}
+  union { // expected-note 0-2{{member not initialized by constructor}}
     char b;
     struct {
       double c;
-      long d; // expected-note {{member not initialized by constructor}}
+      long d; // expected-note 0-1{{member not initialized by constructor}}
     };
     union {
       char e;
       void *f;
     };
   };
-  struct { // expected-note {{member not initialized by constructor}}
+  struct { // expected-note 0-1{{member not initialized by constructor}}
     long long g;
     struct {
-      int h; // expected-note {{member not initialized by constructor}}
-      double i; // expected-note {{member not initialized by constructor}}
+      int h; // expected-note 0-1{{member not initialized by constructor}}
+      double i; // expected-note 0-1{{member not initialized by constructor}}
     };
-    union { // expected-note 2{{member not initialized by constructor}}
+    union { // expected-note 0-2{{member not initialized by constructor}}
       char *j;
       AnonMembers *k;
     };
@@ -166,14 +170,26 @@ struct AnonMembers {
 
   constexpr AnonMembers(int(&)[1]) : a(), b(), g(), h(), i(), j() {} // ok
   // missing d, i, j/k union
-  constexpr AnonMembers(int(&)[2]) : a(), c(), g(), h() {} // expected-error {{constexpr constructor must initialize all members}}
+  constexpr AnonMembers(int(&)[2]) : a(), c(), g(), h() {}
+#ifndef CXX2A
+  // expected-error@-2 {{constexpr constructor that does not initialize all members}}
+#endif
   constexpr AnonMembers(int(&)[3]) : a(), e(), g(), h(), i(), k() {} // ok
   // missing h, j/k union
-  constexpr AnonMembers(int(&)[4]) : a(), c(), d(), g(), i() {} // expected-error {{constexpr constructor must initialize all members}}
+  constexpr AnonMembers(int(&)[4]) : a(), c(), d(), g(), i() {}
+#ifndef CXX2A
+  // expected-error@-2 {{constexpr constructor that does not initialize all members}}
+#endif
   // missing b/c/d/e/f union
-  constexpr AnonMembers(int(&)[5]) : a(), g(), h(), i(), k() {} // expected-error {{constexpr constructor must initialize all members}}
+  constexpr AnonMembers(int(&)[5]) : a(), g(), h(), i(), k() {}
+#ifndef CXX2A
+  // expected-error@-2 {{constexpr constructor that does not initialize all members}}
+#endif
   // missing a, b/c/d/e/f union, g/h/i/j/k struct
-  constexpr AnonMembers(int(&)[6]) {} // expected-error {{constexpr constructor must initialize all members}}
+  constexpr AnonMembers(int(&)[6]) {}
+#ifndef CXX2A
+  // expected-error@-2 {{constexpr constructor that does not initialize all members}}
+#endif
 };
 
 union Empty {
@@ -253,14 +269,20 @@ struct X {
   constexpr X(int c) : a(c) {} // ok, b initialized by 2 * c + 1
 };
 
-union XU1 { int a; constexpr XU1() = default; }; // expected-error{{not constexpr}}
+union XU1 { int a; constexpr XU1() = default; };
+#ifndef CXX2A
+// expected-error@-2{{not constexpr}}
+#endif
 union XU2 { int a = 1; constexpr XU2() = default; };
 
 struct XU3 {
   union {
     int a;
   };
-  constexpr XU3() = default; // expected-error{{not constexpr}}
+  constexpr XU3() = default;
+#ifndef CXX2A
+  // expected-error@-2{{not constexpr}}
+#endif
 };
 struct XU4 {
   union {
index eb086178fc06f5f16ee7f3168b3dad85854613d4..52129844c4188b4a5bd95f38873477254fefaa3f 100644 (file)
@@ -1,7 +1,8 @@
 // RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++1z %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
 
 #if __cplusplus < 201103L
 // expected-no-diagnostics
@@ -38,18 +39,24 @@ namespace dr1460 { // dr1460: 3.5
   }
 
   union A {};
-  union B { int n; }; // expected-note +{{here}}
+  union B { int n; }; // expected-note 0+{{here}}
   union C { int n = 0; };
   struct D { union {}; }; // expected-error {{does not declare anything}}
-  struct E { union { int n; }; }; // expected-note +{{here}}
+  struct E { union { int n; }; }; // expected-note 0+{{here}}
   struct F { union { int n = 0; }; };
 
   struct X {
     friend constexpr A::A() noexcept;
-    friend constexpr B::B() noexcept; // expected-error {{follows non-constexpr declaration}}
+    friend constexpr B::B() noexcept;
+#if __cplusplus <= 201703L
+    // expected-error@-2 {{follows non-constexpr declaration}}
+#endif
     friend constexpr C::C() noexcept;
     friend constexpr D::D() noexcept;
-    friend constexpr E::E() noexcept; // expected-error {{follows non-constexpr declaration}}
+    friend constexpr E::E() noexcept;
+#if __cplusplus <= 201703L
+    // expected-error@-2 {{follows non-constexpr declaration}}
+#endif
     friend constexpr F::F() noexcept;
   };
 
@@ -64,37 +71,61 @@ namespace dr1460 { // dr1460: 3.5
 
   namespace Defaulted {
     union A { constexpr A() = default; };
-    union B { int n; constexpr B() = default; }; // expected-error {{not constexpr}}
+    union B { int n; constexpr B() = default; };
+#if __cplusplus <= 201703L
+    // expected-error@-2 {{not constexpr}}
+#endif
     union C { int n = 0; constexpr C() = default; };
     struct D { union {}; constexpr D() = default; }; // expected-error {{does not declare anything}}
-    struct E { union { int n; }; constexpr E() = default; }; // expected-error {{not constexpr}}
+    struct E { union { int n; }; constexpr E() = default; };
+#if __cplusplus <= 201703L
+    // expected-error@-2 {{not constexpr}}
+#endif
     struct F { union { int n = 0; }; constexpr F() = default; };
 
-    struct G { union { int n = 0; }; union { int m; }; constexpr G() = default; }; // expected-error {{not constexpr}}
+    struct G { union { int n = 0; }; union { int m; }; constexpr G() = default; };
+#if __cplusplus <= 201703L
+    // expected-error@-2 {{not constexpr}}
+#endif
     struct H {
       union {
         int n = 0;
       };
-      union { // expected-note 2{{member not initialized}}
+      union { // expected-note 0-2{{member not initialized}}
         int m;
       };
-      constexpr H() {} // expected-error {{must initialize all members}}
+      constexpr H() {}
+#if __cplusplus <= 201703L
+      // expected-error@-2 {{initialize all members}}
+#endif
       constexpr H(bool) : m(1) {}
-      constexpr H(char) : n(1) {} // expected-error {{must initialize all members}}
+      constexpr H(char) : n(1) {}
+#if __cplusplus <= 201703L
+      // expected-error@-2 {{initialize all members}}
+#endif
       constexpr H(double) : m(1), n(1) {}
     };
   }
 
 #if __cplusplus > 201103L
   template<typename T> constexpr bool check() {
-    T t; // expected-note-re 2{{non-constexpr constructor '{{[BE]}}'}}
+    T t;
+#if __cplusplus <= 201703L
+    // expected-note-re@-2 2{{non-constexpr constructor '{{[BE]}}'}}
+#endif
     return true;
   }
   static_assert(check<A>(), "");
-  static_assert(check<B>(), ""); // expected-error {{constant}} expected-note {{in call}}
+  static_assert(check<B>(), "");
+#if __cplusplus <= 201703L
+  // expected-error@-2 {{constant}} expected-note@-2 {{in call}}
+#endif
   static_assert(check<C>(), "");
   static_assert(check<D>(), "");
-  static_assert(check<E>(), ""); // expected-error {{constant}} expected-note {{in call}}
+  static_assert(check<E>(), "");
+#if __cplusplus <= 201703L
+  // expected-error@-2 {{constant}} expected-note@-2 {{in call}}
+#endif
   static_assert(check<F>(), "");
 #endif
 
index bcfb3ac5e9550816a7ca79acf83fda14de9f7b94..bd7094f4d4f20e36a9fe9c5d716300e16af5791f 100644 (file)
@@ -1889,9 +1889,10 @@ namespace ConstexprConstructorRecovery {
       };
       constexpr X() noexcept {};
   protected:
-      E val{0}; // expected-error {{cannot initialize a member subobject of type 'ConstexprConstructorRecovery::X::E' with an rvalue of type 'int'}}
+      E val{0}; // expected-error {{cannot initialize a member subobject of type 'ConstexprConstructorRecovery::X::E' with an rvalue of type 'int'}} expected-note {{here}}
   };
-  constexpr X x{};
+  // FIXME: We should avoid issuing this follow-on diagnostic.
+  constexpr X x{}; // expected-error {{constant expression}} expected-note {{not initialized}}
 }
 
 namespace Lifetime {
@@ -2036,7 +2037,7 @@ namespace BadDefaultInit {
   // here is bogus (we discard the k(k) initializer because the parameter 'k'
   // has been marked invalid).
   struct B { // expected-note 2{{candidate}}
-    constexpr B( // expected-error {{must initialize all members}} expected-note {{candidate}}
+    constexpr B( // expected-warning {{initialize all members}} expected-note {{candidate}}
         int k = X<B().k>::n) : // expected-error {{no matching constructor}}
       k(k) {}
     int k; // expected-note {{not initialized}}
index 1f9cb57492584933fd1e707dbfaf932cb79c2b3c..1d4549b618232fc75457ee05f149fb7f273a12ce 100644 (file)
@@ -810,9 +810,10 @@ namespace StmtExpr {
   }
   static_assert(f(1) == 1, ""); // expected-error {{constant expression}} expected-note {{in call}}
 
-  constexpr int g() { // expected-error {{never produces a constant}}
-    return ({ int n; n; }); // expected-note {{object of type 'int' is not initialized}}
+  constexpr int g() {
+    return ({ int n; n; }); // expected-note {{read of uninitialized object}}
   }
+  static_assert(g() == 0, ""); // expected-error {{constant expression}} expected-note {{in call}}
 
   // FIXME: We should handle the void statement expression case.
   constexpr int h() { // expected-error {{never produces a constant}}
index dba877ff2111ad666a841eacccb601a4aa649be0..ec4263e3ee427080aaf966bcb328fb07e50a8bd7 100644 (file)
@@ -416,7 +416,7 @@ namespace TypeId {
 
 namespace Union {
   struct Base {
-    int y; // expected-note {{here}}
+    int y; // expected-note 2{{here}}
   };
   struct A : Base {
     int x;
@@ -489,13 +489,15 @@ namespace Union {
   constexpr A return_uninit_struct() {
     B b = {.b = 1};
     b.a.x = 2;
-    return b.a;
+    return b.a; // expected-note {{in call to 'A(b.a)'}} expected-note {{subobject of type 'int' is not initialized}}
   }
-  // FIXME: It's unclear that this should be valid. Copying a B involves
-  // copying the object representation of the union, but copying an A invokes a
-  // copy constructor that copies the object elementwise, and reading from
-  // b.a.y is undefined.
-  static_assert(return_uninit_struct().x == 2);
+  // Note that this is rejected even though return_uninit() is accepted, and
+  // return_uninit() copies the same stuff wrapped in a union.
+  //
+  // Copying a B involves copying the object representation of the union, but
+  // copying an A invokes a copy constructor that copies the object
+  // elementwise, and reading from b.a.y is undefined.
+  static_assert(return_uninit_struct().x == 2); // expected-error {{constant expression}} expected-note {{in call}}
   constexpr B return_init_all() {
     B b = {.b = 1};
     b.a.x = 2;
@@ -548,3 +550,74 @@ namespace TwosComplementShifts {
   static_assert(-3 >> 1 == -2);
   static_assert(-4 >> 1 == -2);
 }
+
+namespace Uninit {
+  constexpr int f(bool init) {
+    int a;
+    if (init)
+      a = 1;
+    return a; // expected-note {{read of uninitialized object}}
+  }
+  static_assert(f(true) == 1);
+  static_assert(f(false) == 1); // expected-error {{constant expression}} expected-note {{in call}}
+
+  struct X {
+    int n; // expected-note {{declared here}}
+    constexpr X(bool init) {
+      if (init) n = 123;
+    }
+  };
+  constinit X x1(true);
+  constinit X x2(false); // expected-error {{constant initializer}} expected-note {{constinit}} expected-note {{subobject of type 'int' is not initialized}}
+
+  struct Y {
+    struct Z { int n; }; // expected-note {{here}}
+    Z z1;
+    Z z2;
+    Z z3;
+    // OK: the lifetime of z1 (and its members) start before the initializer of
+    // z2 runs.
+    constexpr Y() : z2{ (z1.n = 1, z1.n + 1) } { z3.n = 3; }
+    // Not OK: z3 is not in its lifetime when the initializer of z2 runs.
+    constexpr Y(int) : z2{
+      (z3.n = 1, // expected-note {{assignment to object outside its lifetime}}
+       z3.n + 1) // expected-warning {{uninitialized}}
+    } { z1.n = 3; }
+    constexpr Y(int, int) : z2{} {}
+  };
+  // FIXME: This is working around clang not implementing DR2026. With that
+  // fixed, we should be able to test this without the injected copy.
+  constexpr Y copy(Y y) { return y; } // expected-note {{in call to 'Y(y)'}} expected-note {{subobject of type 'int' is not initialized}}
+  constexpr Y y1 = copy(Y());
+  static_assert(y1.z1.n == 1 && y1.z2.n == 2 && y1.z3.n == 3);
+
+  constexpr Y y2 = copy(Y(0)); // expected-error {{constant expression}} expected-note {{in call}}
+
+  static_assert(Y(0,0).z2.n == 0);
+  static_assert(Y(0,0).z1.n == 0); // expected-error {{constant expression}} expected-note {{read of uninitialized object}}
+  static_assert(Y(0,0).z3.n == 0); // expected-error {{constant expression}} expected-note {{read of uninitialized object}}
+
+  static_assert(copy(Y(0,0)).z2.n == 0); // expected-error {{constant expression}} expected-note {{in call}}
+
+  constexpr unsigned char not_even_unsigned_char() {
+    unsigned char c;
+    return c; // expected-note {{read of uninitialized object}}
+  }
+  constexpr unsigned char x = not_even_unsigned_char(); // expected-error {{constant expression}} expected-note {{in call}}
+
+  constexpr int switch_var(int n) {
+    switch (n) {
+    case 1:
+      int a;
+      a = n;
+      return a;
+
+    case 2:
+      a = n;
+      return a;
+    }
+  }
+  constexpr int s1 = switch_var(1);
+  constexpr int s2 = switch_var(2);
+  static_assert(s1 == 1 && s2 == 2);
+}
index 151192b2ff28c9a161b2f0fd5621bc0fe83504f4..7b2c79326efe9fa1cbd8fb1256069c50c7c30a13 100755 (executable)
@@ -1003,11 +1003,10 @@ as the draft C++2a standard evolves.
       </tr>
       <tr> <!-- from Cologne -->
         <td><a href="http://wg21.link/p1331r2">P1331R2</a></td>
-        <td class="none" align="center">No</td>
+        <td rowspan="2" class="svn" align="center">SVN</td>
       </tr>
       <tr>
         <td><a href="http://wg21.link/p1668r1">P1668R1</a></td>
-        <td class="svn" align="center">Clang 9</td>
       </tr>
       <tr>
         <td><a href="http://wg21.link/p0784r7">P0784R7</a></td>