Author: rsmith Date: Wed Sep 18 10:37:44 2019 New Revision: 372237 URL: http://llvm.org/viewvc/llvm-project?rev=372237&view=rev Log: [c++20] P1331R2: Allow transient use of uninitialized objects in constant evaluation.
Modified: cfe/trunk/include/clang/AST/DeclCXX.h cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/lib/AST/DeclCXX.cpp cfe/trunk/lib/AST/ExprConstant.cpp cfe/trunk/lib/AST/Interp/State.h cfe/trunk/lib/Sema/SemaDeclCXX.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp cfe/trunk/test/CXX/drs/dr14xx.cpp cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp cfe/trunk/www/cxx_status.html Modified: cfe/trunk/include/clang/AST/DeclCXX.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/DeclCXX.h (original) +++ cfe/trunk/include/clang/AST/DeclCXX.h Wed Sep 18 10:37:44 2019 @@ -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. Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Wed Sep 18 10:37:44 2019 @@ -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 : N "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 : 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">; Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Sep 18 10:37:44 2019 @@ -2438,9 +2438,13 @@ def err_constexpr_local_var_static : Err 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 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 "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< Modified: cfe/trunk/lib/AST/DeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/lib/AST/DeclCXX.cpp (original) +++ cfe/trunk/lib/AST/DeclCXX.cpp Wed Sep 18 10:37:44 2019 @@ -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: Modified: cfe/trunk/lib/AST/ExprConstant.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/lib/AST/ExprConstant.cpp (original) +++ cfe/trunk/lib/AST/ExprConstant.cpp Wed Sep 18 10:37:44 2019 @@ -1192,9 +1192,14 @@ APValue &CallStackFrame::createTemporary 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 A /// 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 &I 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, 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, 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 // 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 // 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 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 : 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 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 /// 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 handleLValueToRValueConversi 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 handleLValueToRValueConversi } 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 handleLValueToRValueConversi } } - 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(Eval 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 &In 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(StmtR 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(StmtR 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(SourceLoc 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 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 #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 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 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 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 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( 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::VisitBuiltinC 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::VisitCXXConstr 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; Modified: cfe/trunk/lib/AST/Interp/State.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Interp/State.h?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/lib/AST/Interp/State.h (original) +++ cfe/trunk/lib/AST/Interp/State.h Wed Sep 18 10:37:44 2019 @@ -25,6 +25,7 @@ namespace clang { /// same set of semantic restrictions. enum AccessKinds { AK_Read, + AK_ReadObjectRepresentation, AK_Assign, AK_Increment, AK_Decrement, Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Sep 18 10:37:44 2019 @@ -1778,7 +1778,8 @@ static bool CheckConstexprDeclStmt(Sema 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 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 CheckConstexprCtorInitialize 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 CheckConstexprCtorInitialize 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(S 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()) { Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp (original) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp Wed Sep 18 10:37:44 2019 @@ -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) { Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp (original) +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp Wed Sep 18 10:37:44 2019 @@ -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 { Modified: cfe/trunk/test/CXX/drs/dr14xx.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr14xx.cpp?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/test/CXX/drs/dr14xx.cpp (original) +++ cfe/trunk/test/CXX/drs/dr14xx.cpp Wed Sep 18 10:37:44 2019 @@ -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 Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original) +++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Wed Sep 18 10:37:44 2019 @@ -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}} Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp (original) +++ cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp Wed Sep 18 10:37:44 2019 @@ -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}} Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp (original) +++ cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp Wed Sep 18 10:37:44 2019 @@ -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); +} Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=372237&r1=372236&r2=372237&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Wed Sep 18 10:37:44 2019 @@ -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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits