diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index 13c106b817974..34471da6d834f 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -297,14 +297,14 @@ Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type, } /// Primitive arrays. -Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, - size_t NumElems, bool IsConst, bool IsTemporary, - bool IsMutable) - : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), - MDSize(MD.value_or(0)), +Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type, + MetadataSize MD, size_t NumElems, bool IsConst, + bool IsTemporary, bool IsMutable, bool IsVolatile) + : Source(D), SourceType(SourceTy), ElemSize(primSize(Type)), + Size(ElemSize * NumElems), MDSize(MD.value_or(0)), AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type), IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), - IsArray(true), CtorFn(getCtorArrayPrim(Type)), + IsVolatile(IsVolatile), IsArray(true), CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)) { assert(Source && "Missing source"); assert(NumElems <= (MaxArrayElemBytes / ElemSize)); diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h index 9046801f4ebef..2648a13642bb7 100644 --- a/clang/lib/AST/ByteCode/Descriptor.h +++ b/clang/lib/AST/ByteCode/Descriptor.h @@ -177,8 +177,9 @@ struct Descriptor final { bool IsVolatile); /// Allocates a descriptor for an array of primitives. - Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems, - bool IsConst, bool IsTemporary, bool IsMutable); + Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type, + MetadataSize MD, size_t NumElems, bool IsConst, bool IsTemporary, + bool IsMutable, bool IsVolatile); /// Allocates a descriptor for an array of primitives of unknown size. Descriptor(const DeclTy &D, PrimType Type, MetadataSize MDSize, bool IsConst, diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.cpp b/clang/lib/AST/ByteCode/DynamicAllocator.cpp index 5f53fac923682..36e0bfd666b74 100644 --- a/clang/lib/AST/ByteCode/DynamicAllocator.cpp +++ b/clang/lib/AST/ByteCode/DynamicAllocator.cpp @@ -39,9 +39,11 @@ Block *DynamicAllocator::allocate(const Expr *Source, PrimType T, Form AllocForm) { // Create a new descriptor for an array of the specified size and // element type. - const Descriptor *D = allocateDescriptor( - Source, T, Descriptor::InlineDescMD, NumElements, /*IsConst=*/false, - /*IsTemporary=*/false, /*IsMutable=*/false); + const Descriptor *D = + allocateDescriptor(Source, nullptr, T, Descriptor::InlineDescMD, + NumElements, /*IsConst=*/false, + /*IsTemporary=*/false, /*IsMutable=*/false, + /*IsVolatile=*/false); return allocate(D, EvalID, AllocForm); } diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index abcf55bfa670d..5e9a20462161b 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1988,7 +1988,7 @@ bool EndLifetimePop(InterpState &S, CodePtr OpPC) { bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E, std::optional ArraySize) { - const Pointer &Ptr = S.Stk.peek(); + Pointer &Ptr = S.Stk.peek(); auto directBaseIsUnion = [](const Pointer &Ptr) -> bool { if (Ptr.isArrayElement()) @@ -2049,8 +2049,8 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E, return false; const auto *NewExpr = cast(E); - QualType StorageType = Ptr.getFieldDesc()->getDataType(S.getASTContext()); const ASTContext &ASTCtx = S.getASTContext(); + QualType StorageType = Ptr.getType(); QualType AllocType; if (ArraySize) { AllocType = ASTCtx.getConstantArrayType( @@ -2061,16 +2061,22 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E, AllocType = NewExpr->getAllocatedType(); } - unsigned StorageSize = 1; - unsigned AllocSize = 1; - if (const auto *CAT = dyn_cast(AllocType)) - AllocSize = CAT->getZExtSize(); - if (const auto *CAT = dyn_cast(StorageType)) - StorageSize = CAT->getZExtSize(); + if (AllocType->isArrayType() && Ptr.isArrayElement() && Ptr.getIndex() == 0) { + // The destination of placement new is pointing to the first element + // of an array. There's a special case in [expr.const]: "[...] if T is an + // array type, to the first element of such an object [...]". Handle + // that case here by using the base of the Pointer. + QualType AllocElementType = + ASTCtx.getAsArrayType(AllocType)->getElementType(); + if (ASTCtx.hasSimilarType(AllocElementType, StorageType)) { + // FIXME: This isn't quite right: it jumps too many levels with + // multi-dimensional arrays. + Ptr = Ptr.getBase(); + StorageType = Ptr.getType(); + } + } - if (AllocSize > StorageSize || - !ASTCtx.hasSimilarType(ASTCtx.getBaseElementType(AllocType), - ASTCtx.getBaseElementType(StorageType))) { + if (!ASTCtx.hasSimilarType(AllocType, StorageType)) { S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_placement_new_wrong_type) << StorageType << AllocType; diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index d49903e36d34a..75c68ec7f126d 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -45,11 +45,13 @@ unsigned Program::createGlobalString(const StringLiteral *S, const Expr *Base) { Base = S; // Create a descriptor for the string. - Descriptor *Desc = allocateDescriptor(Base, *CharType, Descriptor::GlobalMD, - StringLength + 1, - /*IsConst=*/true, - /*isTemporary=*/false, - /*isMutable=*/false); + Descriptor *Desc = + allocateDescriptor(Base, S->getType().getTypePtr(), *CharType, + Descriptor::GlobalMD, StringLength + 1, + /*IsConst=*/true, + /*isTemporary=*/false, + /*isMutable=*/false, + /*IsVolatile=*/false); // Allocate storage for the string. // The byte length does not include the null terminator. @@ -423,8 +425,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, if ((Descriptor::MaxArrayElemBytes / ElemSize) < NumElems) { return nullptr; } - return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary, - IsMutable); + return allocateDescriptor(D, CAT, *T, MDSize, NumElems, IsConst, + IsTemporary, IsMutable, IsVolatile); } // Arrays of composites. In this case, the array is a list of pointers, // followed by the actual elements. @@ -469,8 +471,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, if (!ElemTy) return nullptr; - return allocateDescriptor(D, *ElemTy, MDSize, 2, IsConst, IsTemporary, - IsMutable); + return allocateDescriptor(D, CT, *ElemTy, MDSize, 2, IsConst, IsTemporary, + IsMutable, IsVolatile); } // Same with vector types. @@ -479,8 +481,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, if (!ElemTy) return nullptr; - return allocateDescriptor(D, *ElemTy, MDSize, VT->getNumElements(), IsConst, - IsTemporary, IsMutable); + return allocateDescriptor(D, VT, *ElemTy, MDSize, VT->getNumElements(), + IsConst, IsTemporary, IsMutable, IsVolatile); } // Same with constant matrix types. @@ -489,8 +491,9 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, if (!ElemTy) return nullptr; - return allocateDescriptor(D, *ElemTy, MDSize, MT->getNumElementsFlattened(), - IsConst, IsTemporary, IsMutable); + return allocateDescriptor(D, MT, *ElemTy, MDSize, + MT->getNumElementsFlattened(), IsConst, + IsTemporary, IsMutable, IsVolatile); } return nullptr; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 4f45fa728c605..d45dd835b805e 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -10866,15 +10866,7 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { return false; // FIXME: Reject the cases where [basic.life]p8 would not permit the // old name of the object to be used to name the new object. - unsigned SubobjectSize = 1; - unsigned AllocSize = 1; - if (auto *CAT = dyn_cast(AllocType)) - AllocSize = CAT->getZExtSize(); - if (auto *CAT = dyn_cast(SubobjType)) - SubobjectSize = CAT->getZExtSize(); - if (SubobjectSize < AllocSize || - !Info.Ctx.hasSimilarType(Info.Ctx.getBaseElementType(SubobjType), - Info.Ctx.getBaseElementType(AllocType))) { + if (!Info.Ctx.hasSimilarType(SubobjType, AllocType)) { Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type) << SubobjType << AllocType; return false; @@ -10892,6 +10884,22 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { } } Handler = {Info, E, AllocType, AK, nullptr}; + if (AllocType->isArrayType() && + Result.Designator.MostDerivedIsArrayElement && + Result.Designator.Entries.back().getAsArrayIndex() == 0) { + // The destination of placement new is pointing to the first element + // of an array. There's a special case in [expr.const]: "[...] if T is an + // array type, to the first element of such an object [...]". Handle + // that case here by dropping the last entry in the designator list. + QualType AllocElementType = + Info.Ctx.getAsArrayType(AllocType)->getElementType(); + if (Info.Ctx.hasSimilarType(AllocElementType, + Result.Designator.MostDerivedType)) { + Result.Designator.truncate(Info.Ctx, Result.Base, + Result.Designator.MostDerivedPathLength - 1); + } + } + CompleteObject Obj = findCompleteObject(Info, E, AK, Result, AllocType); if (!Obj || !findSubobject(Info, E, Obj, Result.Designator, Handler)) return false; diff --git a/clang/test/AST/ByteCode/placement-new.cpp b/clang/test/AST/ByteCode/placement-new.cpp index 5bad616a0d359..4a0253d24ee83 100644 --- a/clang/test/AST/ByteCode/placement-new.cpp +++ b/clang/test/AST/ByteCode/placement-new.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++2c -fexperimental-new-constant-interpreter -verify=expected,both %s -DBYTECODE +// RUN: %clang_cc1 -std=c++2c -fexperimental-new-constant-interpreter -verify=expected,both %s // RUN: %clang_cc1 -std=c++2c -verify=ref,both %s typedef __INT64_TYPE__ int64_t; @@ -59,24 +59,6 @@ consteval auto ok4() { } static_assert(ok4() == 37); -consteval int ok5() { - int i; - new (&i) int[1]{1}; - - struct S { - int a; int b; - } s; - new (&s) S[1]{{12, 13}}; - - /// FIXME: Broken in the current interpreter. -#if BYTECODE - return s.a + s.b; -#else - return 25; -#endif -} -static_assert(ok5() == 25); - consteval int ok6() { int i[2]; new (i) int(100); @@ -85,23 +67,6 @@ consteval int ok6() { } static_assert(ok6() == 300); -/// FIXME: Broken in the current interpreter. -#if BYTECODE -consteval int ok7() { - int i; - new (&i) int[1]{1}; - return i; -} -static_assert(ok7() == 1); - -consteval int ok8() { - int i[2]; - new (&i) int(100); - return i[0]; -} -static_assert(ok8() == 100); -#endif - consteval auto fail1() { int b; new (&b) float(1.0); // both-note {{placement new would change type of storage from 'int' to 'float'}} @@ -117,6 +82,29 @@ consteval int fail2() { } static_assert(fail2() == 0); // both-error {{not an integral constant expression}} \ // both-note {{in call to}} +consteval int fail3() { + int i; + new (&i) int[1]{1}; // both-note {{placement new would change type of storage from 'int' to 'int[1]'}} + return 0; +} +static_assert(fail3() == 0); // both-error {{not an integral constant expression}} \ + // both-note {{in call to}} +consteval int fail4() { + struct S { + int a; int b; + } s; + new (&s) S[1]{{12, 13}}; // both-note {{placement new would change type of storage from 'struct S' to 'S[1]'}} + return 0; +} +static_assert(fail4() == 0); // both-error {{not an integral constant expression}} \ + // both-note {{in call to}} +consteval int fail5() { + int i[2]; + new (&i) int[]{12}; // both-note {{placement new would change type of storage from 'int[2]' to 'int[1]'}} + return i[0]; +} +static_assert(fail5() == 12); // both-error {{not an integral constant expression}} \ + // both-note {{in call to}} consteval int indeterminate() { int * indeterminate; @@ -147,14 +135,26 @@ consteval int array3() { } static_assert(array3() == 0); // both-error {{not an integral constant expression}} \ // both-note {{in call to}} - consteval int array4() { int i[2]; - new (&i) int[]{12}; + new (i) int[2]{12,13}; return i[0]; } static_assert(array4() == 12); - +consteval int array5() { + int i[2][2]; + new (i) int[2][2]{12,13}; + return i[0][0]; +} +static_assert(array5() == 12); +consteval int array6() { + int i[2][2]; + // FIXME: Wrong diagnostic; see CheckNewTypeMismatch + new (i[1]) int[2]{12,13}; // expected-note {{placement new would change type of storage from 'int[2][2]' to 'int[2]'}} + return i[1][0]; +} +static_assert(array6() == 12); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} constexpr int *intptr() { return new int; } @@ -236,6 +236,26 @@ namespace records { } static_assert(record4()); + constexpr bool record5() { + S ss[3][3]; + + new (ss) S[3][3]{1,2,3,4,5,6,7,8,9}; + + return ss[0][0].f == 1 && ss[0][1].f == 2 && ss[0][2].f == 3; + } + static_assert(record5()); + + constexpr bool record6() { + S ss[3][3]; + + // FIXME: Wrong diagnostic; see CheckNewTypeMismatch + new (ss[1]) S[3]{1,2,3}; // expected-note {{placement new would change type of storage from 'S[3][3]' to 'S[3]'}} + + return ss[1][0].f == 1 && ss[1][1].f == 2 && ss[1][2].f == 3; + } + static_assert(record6()); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} + /// Destructor is NOT called. struct A { bool b; @@ -367,19 +387,16 @@ namespace ExplicitThisOnArrayElement { static_assert(foo()); // both-error {{not an integral constant expression}} } -#ifdef BYTECODE -constexpr int N = [] // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{assignment to dereferenced one-past-the-end pointer is not allowed in a constant expression}} \ - // expected-note {{in call to}} +constexpr int N = [] // both-error {{must be initialized by a constant expression}} \ + // both-note {{in call to}} { struct S { int a[1]; }; S s; - ::new (s.a) int[1][2][3][4](); + ::new (s.a) int[1][2][3][4](); // both-note {{placement new would change type of storage from 'int' to 'int[1][2][3][4]'}} return s.a[0]; }(); -#endif namespace MemMove { constexpr int foo() { diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp b/clang/test/SemaCXX/constant-expression-p2280r4.cpp index f4a8fbcbbe427..11d4b1e6ca008 100644 --- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp +++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp @@ -292,7 +292,7 @@ namespace unsized_array { // interpreter-note {{arithmetic involving unrelated objects 'a' and 'b' has unspecified value}} constexpr int t3 = a - &c[2]; // expected-error {{constexpr variable 't3' must be initialized by a constant expression}} \ // nointerpreter-note {{arithmetic involving unrelated objects '&a[0]' and '&c[2]' has unspecified value}} \ - // interpreter-note {{arithmetic involving unrelated objects 'a' and '*((char*)&c + 8)' has unspecified value}} + // interpreter-note {{arithmetic involving unrelated objects 'a' and '&c + 2' has unspecified value}} } } diff --git a/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp index 4cf0e9ffe1d64..f20e7f5ff1d98 100644 --- a/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp +++ b/clang/test/SemaCXX/cxx2c-constexpr-placement-new.cpp @@ -13,7 +13,6 @@ void* operator new[] (std::size_t, void* p) {return p;} consteval int ok() { int i; new (&i) int(0); - new (&i) int[1]{1}; new (static_cast(&i)) int(0); return 0; } @@ -35,10 +34,7 @@ consteval int indeterminate() { consteval int array1() { int i[2]; new (&i) int[]{1,2}; - new (&i) int[]{1}; - new (&i) int(0); new (static_cast(&i)) int[]{1,2}; - new (static_cast(&i)) int[]{1}; return 0; } @@ -49,6 +45,30 @@ consteval int array2() { return 0; } +consteval int array3() { + int i; + new (&i) int[1]{1}; // expected-note {{placement new would change type of storage from 'int' to 'int[1]'}} + return 0; +} + +consteval int array4() { + int i[2]; + new (&i) int[]{1}; // expected-note {{placement new would change type of storage from 'int[2]' to 'int[1]'}} + return 0; +} + +consteval int array5() { + int i[2]; + new (&i) int(0); // expected-note {{placement new would change type of storage from 'int[2]' to 'int'}} + return 0; +} + +consteval int array6() { + int i[2]; + new (static_cast(&i)) int[]{1}; // expected-note {{placement new would change type of storage from 'int[2]' to 'int[1]'}} + return 0; +} + struct S{ int* i; constexpr S() : i(new int(42)) {} // #no-deallocation @@ -79,6 +99,15 @@ int c = indeterminate(); // expected-error {{call to consteval function 'indeter int d = array1(); int e = array2(); // expected-error {{call to consteval function 'array2' is not a constant expression}} \ // expected-note {{in call to 'array2()'}} +int f = array3(); // expected-error {{call to consteval function 'array3' is not a constant expression}} \ + // expected-note {{in call to 'array3()'}} +int g = array4(); // expected-error {{call to consteval function 'array4' is not a constant expression}} \ + // expected-note {{in call to 'array4()'}} +int h = array5(); // expected-error {{call to consteval function 'array5' is not a constant expression}} \ + // expected-note {{in call to 'array5()'}} +int i = array6(); // expected-error {{call to consteval function 'array6' is not a constant expression}} \ + // expected-note {{in call to 'array6()'}} + int alloc1 = (alloc(), 0); int alloc2 = (alloc_err(), 0); // expected-error {{call to consteval function 'alloc_err' is not a constant expression}} // expected-note@#no-deallocation {{allocation performed here was not deallocated}}