diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -4312,37 +4312,48 @@ } /// Get the value to use for a default-initialized object of type T. -static APValue getDefaultInitValue(QualType T) { +/// Return false if fails. +static bool getDefaultInitValue(QualType T, APValue &Result) { + bool Success = true; 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())); + if (RD->isInvalidDecl()) { + Result = APValue(); + return false; + } + if (RD->isUnion()) { + Result = APValue((const FieldDecl *)nullptr); + return true; + } + Result = APValue(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()); + End = RD->bases_end(); + I != End; ++I, ++Index) + Success &= getDefaultInitValue(I->getType(), Result.getStructBase(Index)); for (const auto *I : RD->fields()) { if (I->isUnnamedBitfield()) continue; - Struct.getStructField(I->getFieldIndex()) = - getDefaultInitValue(I->getType()); + Success &= getDefaultInitValue(I->getType(), + Result.getStructField(I->getFieldIndex())); } - return Struct; + return Success; } if (auto *AT = dyn_cast_or_null(T->getAsArrayTypeUnsafe())) { - APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue()); - if (Array.hasArrayFiller()) - Array.getArrayFiller() = getDefaultInitValue(AT->getElementType()); - return Array; + Result = APValue(APValue::UninitArray(), 0, AT->getSize().getZExtValue()); + if (Result.hasArrayFiller()) + Success &= + getDefaultInitValue(AT->getElementType(), Result.getArrayFiller()); + + return Success; } - return APValue::IndeterminateValue(); + Result = APValue::IndeterminateValue(); + return true; } namespace { @@ -4372,10 +4383,8 @@ Info.CurrentCall->createTemporary(VD, VD->getType(), true, Result); const Expr *InitE = VD->getInit(); - if (!InitE) { - Val = getDefaultInitValue(VD->getType()); - return true; - } + if (!InitE) + return getDefaultInitValue(VD->getType(), Val); if (InitE->isValueDependent()) return false; @@ -5372,11 +5381,11 @@ const Expr *LHSExpr; const FieldDecl *Field; bool DuringInit; - + bool Failed = false; static const AccessKinds AccessKind = AK_Assign; typedef bool result_type; - bool failed() { return false; } + bool failed() { return Failed; } bool found(APValue &Subobj, QualType SubobjType) { // We are supposed to perform no initialization but begin the lifetime of // the object. We interpret that as meaning to do what default @@ -5400,8 +5409,9 @@ diag::note_constexpr_union_member_change_during_init); return false; } - - Subobj.setUnion(Field, getDefaultInitValue(Field->getType())); + APValue Result; + Failed = !getDefaultInitValue(Field->getType(), Result); + Subobj.setUnion(Field, Result); return true; } bool found(APSInt &Value, QualType SubobjType) { @@ -5717,8 +5727,9 @@ for (; !declaresSameEntity(*FieldIt, FD); ++FieldIt) { assert(FieldIt != RD->field_end() && "missing field?"); if (!FieldIt->isUnnamedBitfield()) - Result.getStructField(FieldIt->getFieldIndex()) = - getDefaultInitValue(FieldIt->getType()); + Success &= getDefaultInitValue( + FieldIt->getType(), + Result.getStructField(FieldIt->getFieldIndex())); } ++FieldIt; }; @@ -5769,11 +5780,11 @@ (Value->isUnion() && Value->getUnionField() != FD)) { if (CD->isUnion()) *Value = APValue(FD); - else - // 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)); + else if (!getDefaultInitValue(Info.Ctx.getRecordType(CD), *Value)) + // 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. + Success = false; } // Store Subobject as its parent before updating it for the last element // in the chain. @@ -5820,8 +5831,9 @@ if (!RD->isUnion()) { for (; FieldIt != RD->field_end(); ++FieldIt) { if (!FieldIt->isUnnamedBitfield()) - Result.getStructField(FieldIt->getFieldIndex()) = - getDefaultInitValue(FieldIt->getType()); + Success &= getDefaultInitValue( + FieldIt->getType(), + Result.getStructField(FieldIt->getFieldIndex())); } } @@ -8907,8 +8919,8 @@ } else if (Init) { if (!EvaluateInPlace(*Val, Info, Result, Init)) return false; - } else { - *Val = getDefaultInitValue(AllocType); + } else if (!getDefaultInitValue(AllocType, *Val)) { + return Error(E); } // Array new returns a pointer to the first element, not a pointer to the @@ -9279,8 +9291,7 @@ if (ZeroInit) return ZeroInitialization(E, T); - Result = getDefaultInitValue(T); - return true; + return getDefaultInitValue(T, Result); } const FunctionDecl *Definition = nullptr; @@ -14012,10 +14023,11 @@ // Make a copy of the value for the destructor to mutate, if we know it. // Otherwise, treat the value as default-initialized; if the destructor works // anyway, then the destruction is constant (and must be essentially empty). - APValue DestroyedValue = - (getEvaluatedValue() && !getEvaluatedValue()->isAbsent()) - ? *getEvaluatedValue() - : getDefaultInitValue(getType()); + APValue DestroyedValue; + if (getEvaluatedValue() && !getEvaluatedValue()->isAbsent()) + DestroyedValue = *getEvaluatedValue(); + else if (!getDefaultInitValue(getType(), DestroyedValue)) + return false; EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression); Info.setEvaluatingDecl(this, DestroyedValue, diff --git a/clang/test/SemaCXX/constexpr-default-init-value-crash.cpp b/clang/test/SemaCXX/constexpr-default-init-value-crash.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/constexpr-default-init-value-crash.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify +// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify -fno-recovery-ast + +namespace NoCrash { +struct ForwardDecl; // expected-note {{forward declaration of}} +struct Foo { // expected-note 2{{candidate constructor}} + ForwardDecl f; // expected-error {{field has incomplete type}} +}; + +constexpr Foo getFoo() { + Foo e = 123; // expected-error {{no viable conversion from 'int' to 'NoCrash::Foo'}} + return e; +} +}