Skip to content

Commit e8d8488

Browse files
authored
[clang][bytecode] Fix dynamic array allocation return values (llvm#127387)
We need to return a pointer to the first element, not the array itself.
1 parent d64cf19 commit e8d8488

File tree

5 files changed

+82
-29
lines changed

5 files changed

+82
-29
lines changed

clang/lib/AST/ByteCode/DynamicAllocator.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Block *DynamicAllocator::allocate(const Expr *Source, PrimType T,
5454
Block *DynamicAllocator::allocate(const Descriptor *ElementDesc,
5555
size_t NumElements, unsigned EvalID,
5656
Form AllocForm) {
57+
assert(ElementDesc->getMetadataSize() == 0);
5758
// Create a new descriptor for an array of the specified size and
5859
// element type.
5960
const Descriptor *D = allocateDescriptor(
@@ -72,6 +73,7 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
7273
auto *B = new (Memory.get()) Block(EvalID, D, /*isStatic=*/false);
7374
B->invokeCtor();
7475

76+
assert(D->getMetadataSize() == sizeof(InlineDescriptor));
7577
InlineDescriptor *ID = reinterpret_cast<InlineDescriptor *>(B->rawData());
7678
ID->Desc = D;
7779
ID->IsActive = true;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2896,9 +2896,7 @@ inline bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
28962896
Block *B = Allocator.allocate(Desc, S.Ctx.getEvalID(),
28972897
DynamicAllocator::Form::NonArray);
28982898
assert(B);
2899-
29002899
S.Stk.push<Pointer>(B);
2901-
29022900
return true;
29032901
}
29042902

@@ -2923,8 +2921,7 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
29232921
Allocator.allocate(Source, T, static_cast<size_t>(NumElements),
29242922
S.Ctx.getEvalID(), DynamicAllocator::Form::Array);
29252923
assert(B);
2926-
S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
2927-
2924+
S.Stk.push<Pointer>(B);
29282925
return true;
29292926
}
29302927

@@ -2950,9 +2947,7 @@ inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc,
29502947
Allocator.allocate(ElementDesc, static_cast<size_t>(NumElements),
29512948
S.Ctx.getEvalID(), DynamicAllocator::Form::Array);
29522949
assert(B);
2953-
2954-
S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
2955-
2950+
S.Stk.push<Pointer>(B);
29562951
return true;
29572952
}
29582953

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,49 +1655,50 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
16551655
return false;
16561656
}
16571657

1658+
bool IsArray = NumElems.ugt(1);
16581659
std::optional<PrimType> ElemT = S.getContext().classify(ElemType);
16591660
DynamicAllocator &Allocator = S.getAllocator();
16601661
if (ElemT) {
1661-
if (NumElems.ule(1)) {
1662-
const Descriptor *Desc =
1663-
S.P.createDescriptor(NewCall, *ElemT, Descriptor::InlineDescMD,
1664-
/*IsConst=*/false, /*IsTemporary=*/false,
1665-
/*IsMutable=*/false);
1666-
Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(),
1662+
if (IsArray) {
1663+
Block *B = Allocator.allocate(NewCall, *ElemT, NumElems.getZExtValue(),
1664+
S.Ctx.getEvalID(),
16671665
DynamicAllocator::Form::Operator);
16681666
assert(B);
1669-
1670-
S.Stk.push<Pointer>(B);
1667+
S.Stk.push<Pointer>(Pointer(B).atIndex(0));
16711668
return true;
16721669
}
1673-
assert(NumElems.ugt(1));
16741670

1675-
Block *B =
1676-
Allocator.allocate(NewCall, *ElemT, NumElems.getZExtValue(),
1677-
S.Ctx.getEvalID(), DynamicAllocator::Form::Operator);
1671+
const Descriptor *Desc =
1672+
S.P.createDescriptor(NewCall, *ElemT, Descriptor::InlineDescMD,
1673+
/*IsConst=*/false, /*IsTemporary=*/false,
1674+
/*IsMutable=*/false);
1675+
Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(),
1676+
DynamicAllocator::Form::Operator);
16781677
assert(B);
1678+
16791679
S.Stk.push<Pointer>(B);
16801680
return true;
16811681
}
16821682

16831683
assert(!ElemT);
16841684
// Structs etc.
16851685
const Descriptor *Desc = S.P.createDescriptor(
1686-
NewCall, ElemType.getTypePtr(), Descriptor::InlineDescMD,
1686+
NewCall, ElemType.getTypePtr(),
1687+
IsArray ? std::nullopt : Descriptor::InlineDescMD,
16871688
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false,
16881689
/*Init=*/nullptr);
16891690

1690-
if (NumElems.ule(1)) {
1691-
Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(),
1692-
DynamicAllocator::Form::Operator);
1691+
if (IsArray) {
1692+
Block *B =
1693+
Allocator.allocate(Desc, NumElems.getZExtValue(), S.Ctx.getEvalID(),
1694+
DynamicAllocator::Form::Operator);
16931695
assert(B);
1694-
S.Stk.push<Pointer>(B);
1696+
S.Stk.push<Pointer>(Pointer(B).atIndex(0));
16951697
return true;
16961698
}
16971699

1698-
Block *B =
1699-
Allocator.allocate(Desc, NumElems.getZExtValue(), S.Ctx.getEvalID(),
1700-
DynamicAllocator::Form::Operator);
1700+
Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(),
1701+
DynamicAllocator::Form::Operator);
17011702
assert(B);
17021703
S.Stk.push<Pointer>(B);
17031704
return true;

clang/lib/AST/ByteCode/Program.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
432432
return allocateDescriptor(D, *T, MDSize, IsTemporary,
433433
Descriptor::UnknownSize{});
434434
} else {
435-
const Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(),
436-
MDSize, IsConst, IsTemporary);
435+
const Descriptor *Desc = createDescriptor(
436+
D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
437437
if (!Desc)
438438
return nullptr;
439439
return allocateDescriptor(D, Desc, MDSize, IsTemporary,
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %clang_cc1 -std=c++2c -fexperimental-new-constant-interpreter -verify=expected,both %s
2+
// RUN: %clang_cc1 -std=c++2c -verify=ref,both %s
3+
4+
5+
/// This example used to cause an invalid read because allocating
6+
/// an array needs to return a pointer to the first element,
7+
/// not to the array.
8+
9+
namespace std {
10+
using size_t = decltype(sizeof(0));
11+
12+
template <class _Tp>
13+
class allocator {
14+
public:
15+
typedef size_t size_type;
16+
typedef _Tp value_type;
17+
constexpr _Tp *allocate(size_t __n) {
18+
return static_cast<_Tp *>(::operator new(__n * sizeof(_Tp)));
19+
}
20+
};
21+
}
22+
23+
void *operator new(std::size_t, void *p) { return p; }
24+
void* operator new[] (std::size_t, void* p) {return p;}
25+
26+
namespace std {
27+
template <class _Ep>
28+
class initializer_list {
29+
const _Ep *__begin_;
30+
__SIZE_TYPE__ __size_;
31+
32+
public:
33+
typedef _Ep value_type;
34+
typedef const _Ep &reference;
35+
constexpr __SIZE_TYPE__ size() const noexcept { return __size_; }
36+
constexpr const _Ep *begin() const noexcept { return __begin_; }
37+
constexpr const _Ep *end() const noexcept { return __begin_ + __size_; }
38+
};
39+
}
40+
41+
template<typename T>
42+
class vector {
43+
public:
44+
constexpr vector(std::initializer_list<T> Ts) {
45+
A = B = std::allocator<T>{}.allocate(Ts.size()); // both-note {{heap allocation performed here}}
46+
47+
new (A) T(*Ts.begin());
48+
}
49+
private:
50+
T *A = nullptr;
51+
T *B = nullptr;
52+
};
53+
54+
constexpr vector<vector<int>> ints = {{3}, {4}}; // both-error {{must be initialized by a constant expression}} \
55+
// both-note {{pointer to}}

0 commit comments

Comments
 (0)