Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/AST/ByteCode/Interp.h
213799 views
1
//===--- Interp.h - Interpreter for the constexpr VM ------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// Definition of the interpreter state and entry point.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#ifndef LLVM_CLANG_AST_INTERP_INTERP_H
14
#define LLVM_CLANG_AST_INTERP_INTERP_H
15
16
#include "../ExprConstShared.h"
17
#include "BitcastBuffer.h"
18
#include "Boolean.h"
19
#include "DynamicAllocator.h"
20
#include "FixedPoint.h"
21
#include "Floating.h"
22
#include "Function.h"
23
#include "InterpBuiltinBitCast.h"
24
#include "InterpFrame.h"
25
#include "InterpStack.h"
26
#include "InterpState.h"
27
#include "MemberPointer.h"
28
#include "Opcode.h"
29
#include "PrimType.h"
30
#include "Program.h"
31
#include "State.h"
32
#include "clang/AST/ASTContext.h"
33
#include "clang/AST/Expr.h"
34
#include "llvm/ADT/APFloat.h"
35
#include "llvm/ADT/APSInt.h"
36
#include <type_traits>
37
38
namespace clang {
39
namespace interp {
40
41
using APSInt = llvm::APSInt;
42
using FixedPointSemantics = llvm::FixedPointSemantics;
43
44
/// Checks if the variable has externally defined storage.
45
bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
46
47
/// Checks if the array is offsetable.
48
bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
49
50
/// Checks if a pointer is live and accessible.
51
bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
52
AccessKinds AK);
53
54
/// Checks if a pointer is a dummy pointer.
55
bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
56
AccessKinds AK);
57
58
/// Checks if a pointer is null.
59
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
60
CheckSubobjectKind CSK);
61
62
/// Checks if a pointer is in range.
63
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
64
AccessKinds AK);
65
66
/// Checks if a field from which a pointer is going to be derived is valid.
67
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
68
CheckSubobjectKind CSK);
69
70
/// Checks if Ptr is a one-past-the-end pointer.
71
bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
72
CheckSubobjectKind CSK);
73
74
/// Checks if the dowcast using the given offset is possible with the given
75
/// pointer.
76
bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
77
uint32_t Offset);
78
79
/// Checks if a pointer points to const storage.
80
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
81
82
/// Checks if the Descriptor is of a constexpr or const global variable.
83
bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc);
84
85
/// Checks if a pointer points to a mutable field.
86
bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
87
88
/// Checks if a value can be loaded from a block.
89
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
90
AccessKinds AK = AK_Read);
91
bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
92
93
bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
94
AccessKinds AK);
95
/// Check if a global variable is initialized.
96
bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
97
98
/// Checks if a value can be stored in a block.
99
bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
100
101
/// Checks if a method can be invoked on an object.
102
bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
103
104
/// Checks if a value can be initialized.
105
bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
106
107
/// Checks if a method can be called.
108
bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F);
109
110
/// Checks if calling the currently active function would exceed
111
/// the allowed call depth.
112
bool CheckCallDepth(InterpState &S, CodePtr OpPC);
113
114
/// Checks the 'this' pointer.
115
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
116
117
/// Checks if all the arguments annotated as 'nonnull' are in fact not null.
118
bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
119
const CallExpr *CE, unsigned ArgSize);
120
121
/// Checks if dynamic memory allocation is available in the current
122
/// language mode.
123
bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC);
124
125
/// Diagnose mismatched new[]/delete or new/delete[] pairs.
126
bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC,
127
DynamicAllocator::Form AllocForm,
128
DynamicAllocator::Form DeleteForm, const Descriptor *D,
129
const Expr *NewExpr);
130
131
/// Check the source of the pointer passed to delete/delete[] has actually
132
/// been heap allocated by us.
133
bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
134
const Pointer &Ptr);
135
136
bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
137
AccessKinds AK);
138
139
/// Sets the given integral value to the pointer, which is of
140
/// a std::{weak,partial,strong}_ordering type.
141
bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
142
const Pointer &Ptr, const APSInt &IntValue);
143
144
/// Copy the contents of Src into Dest.
145
bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest);
146
147
bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
148
uint32_t VarArgSize);
149
bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
150
uint32_t VarArgSize);
151
bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
152
uint32_t VarArgSize);
153
bool CallBI(InterpState &S, CodePtr OpPC, const CallExpr *CE,
154
uint32_t BuiltinID);
155
bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
156
const CallExpr *CE);
157
bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T);
158
bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index);
159
bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
160
bool TargetIsUCharOrByte);
161
bool CheckBCPResult(InterpState &S, const Pointer &Ptr);
162
bool CheckDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
163
164
template <typename T>
165
static bool handleOverflow(InterpState &S, CodePtr OpPC, const T &SrcValue) {
166
const Expr *E = S.Current->getExpr(OpPC);
167
S.CCEDiag(E, diag::note_constexpr_overflow) << SrcValue << E->getType();
168
return S.noteUndefinedBehavior();
169
}
170
bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
171
const FixedPoint &FP);
172
173
bool isConstexprUnknown(const Pointer &P);
174
175
inline bool CheckArraySize(InterpState &S, CodePtr OpPC, uint64_t NumElems);
176
177
enum class ShiftDir { Left, Right };
178
179
/// Checks if the shift operation is legal.
180
template <ShiftDir Dir, typename LT, typename RT>
181
bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS,
182
unsigned Bits) {
183
if (RHS.isNegative()) {
184
const SourceInfo &Loc = S.Current->getSource(OpPC);
185
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
186
if (!S.noteUndefinedBehavior())
187
return false;
188
}
189
190
// C++11 [expr.shift]p1: Shift width must be less than the bit width of
191
// the shifted type.
192
if (Bits > 1 && RHS >= Bits) {
193
const Expr *E = S.Current->getExpr(OpPC);
194
const APSInt Val = RHS.toAPSInt();
195
QualType Ty = E->getType();
196
S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
197
if (!S.noteUndefinedBehavior())
198
return false;
199
}
200
201
if constexpr (Dir == ShiftDir::Left) {
202
if (LHS.isSigned() && !S.getLangOpts().CPlusPlus20) {
203
// C++11 [expr.shift]p2: A signed left shift must have a non-negative
204
// operand, and must not overflow the corresponding unsigned type.
205
if (LHS.isNegative()) {
206
const Expr *E = S.Current->getExpr(OpPC);
207
S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << LHS.toAPSInt();
208
if (!S.noteUndefinedBehavior())
209
return false;
210
} else if (LHS.toUnsigned().countLeadingZeros() <
211
static_cast<unsigned>(RHS)) {
212
const Expr *E = S.Current->getExpr(OpPC);
213
S.CCEDiag(E, diag::note_constexpr_lshift_discards);
214
if (!S.noteUndefinedBehavior())
215
return false;
216
}
217
}
218
}
219
220
// C++2a [expr.shift]p2: [P0907R4]:
221
// E1 << E2 is the unique value congruent to
222
// E1 x 2^E2 module 2^N.
223
return true;
224
}
225
226
/// Checks if Div/Rem operation on LHS and RHS is valid.
227
template <typename T>
228
bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
229
if (RHS.isZero()) {
230
const auto *Op = cast<BinaryOperator>(S.Current->getExpr(OpPC));
231
if constexpr (std::is_same_v<T, Floating>) {
232
S.CCEDiag(Op, diag::note_expr_divide_by_zero)
233
<< Op->getRHS()->getSourceRange();
234
return true;
235
}
236
237
S.FFDiag(Op, diag::note_expr_divide_by_zero)
238
<< Op->getRHS()->getSourceRange();
239
return false;
240
}
241
242
if constexpr (!std::is_same_v<T, FixedPoint>) {
243
if (LHS.isSigned() && LHS.isMin() && RHS.isNegative() && RHS.isMinusOne()) {
244
APSInt LHSInt = LHS.toAPSInt();
245
SmallString<32> Trunc;
246
(-LHSInt.extend(LHSInt.getBitWidth() + 1)).toString(Trunc, 10);
247
const SourceInfo &Loc = S.Current->getSource(OpPC);
248
const Expr *E = S.Current->getExpr(OpPC);
249
S.CCEDiag(Loc, diag::note_constexpr_overflow) << Trunc << E->getType();
250
return false;
251
}
252
}
253
return true;
254
}
255
256
template <typename SizeT>
257
bool CheckArraySize(InterpState &S, CodePtr OpPC, SizeT *NumElements,
258
unsigned ElemSize, bool IsNoThrow) {
259
// FIXME: Both the SizeT::from() as well as the
260
// NumElements.toAPSInt() in this function are rather expensive.
261
262
// Can't be too many elements if the bitwidth of NumElements is lower than
263
// that of Descriptor::MaxArrayElemBytes.
264
if ((NumElements->bitWidth() - NumElements->isSigned()) <
265
(sizeof(Descriptor::MaxArrayElemBytes) * 8))
266
return true;
267
268
// FIXME: GH63562
269
// APValue stores array extents as unsigned,
270
// so anything that is greater that unsigned would overflow when
271
// constructing the array, we catch this here.
272
SizeT MaxElements = SizeT::from(Descriptor::MaxArrayElemBytes / ElemSize);
273
assert(MaxElements.isPositive());
274
if (NumElements->toAPSInt().getActiveBits() >
275
ConstantArrayType::getMaxSizeBits(S.getASTContext()) ||
276
*NumElements > MaxElements) {
277
if (!IsNoThrow) {
278
const SourceInfo &Loc = S.Current->getSource(OpPC);
279
280
if (NumElements->isSigned() && NumElements->isNegative()) {
281
S.FFDiag(Loc, diag::note_constexpr_new_negative)
282
<< NumElements->toDiagnosticString(S.getASTContext());
283
} else {
284
S.FFDiag(Loc, diag::note_constexpr_new_too_large)
285
<< NumElements->toDiagnosticString(S.getASTContext());
286
}
287
}
288
return false;
289
}
290
return true;
291
}
292
293
/// Checks if the result of a floating-point operation is valid
294
/// in the current context.
295
bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
296
APFloat::opStatus Status, FPOptions FPO);
297
298
/// Checks why the given DeclRefExpr is invalid.
299
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR);
300
301
/// Interpreter entry point.
302
bool Interpret(InterpState &S);
303
304
/// Interpret a builtin function.
305
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
306
uint32_t BuiltinID);
307
308
/// Interpret an offsetof operation.
309
bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E,
310
ArrayRef<int64_t> ArrayIndices, int64_t &Result);
311
312
inline bool Invalid(InterpState &S, CodePtr OpPC);
313
314
enum class ArithOp { Add, Sub };
315
316
//===----------------------------------------------------------------------===//
317
// Returning values
318
//===----------------------------------------------------------------------===//
319
320
void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC,
321
const Function *Func);
322
323
template <PrimType Name, class T = typename PrimConv<Name>::T>
324
bool Ret(InterpState &S, CodePtr &PC) {
325
const T &Ret = S.Stk.pop<T>();
326
327
assert(S.Current);
328
assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
329
if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
330
cleanupAfterFunctionCall(S, PC, S.Current->getFunction());
331
332
if (InterpFrame *Caller = S.Current->Caller) {
333
PC = S.Current->getRetPC();
334
InterpFrame::free(S.Current);
335
S.Current = Caller;
336
S.Stk.push<T>(Ret);
337
} else {
338
InterpFrame::free(S.Current);
339
S.Current = nullptr;
340
// The topmost frame should come from an EvalEmitter,
341
// which has its own implementation of the Ret<> instruction.
342
}
343
return true;
344
}
345
346
inline bool RetVoid(InterpState &S, CodePtr &PC) {
347
assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
348
349
if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
350
cleanupAfterFunctionCall(S, PC, S.Current->getFunction());
351
352
if (InterpFrame *Caller = S.Current->Caller) {
353
PC = S.Current->getRetPC();
354
InterpFrame::free(S.Current);
355
S.Current = Caller;
356
} else {
357
InterpFrame::free(S.Current);
358
S.Current = nullptr;
359
}
360
return true;
361
}
362
363
//===----------------------------------------------------------------------===//
364
// Add, Sub, Mul
365
//===----------------------------------------------------------------------===//
366
367
template <typename T, bool (*OpFW)(T, T, unsigned, T *),
368
template <typename U> class OpAP>
369
bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS,
370
const T &RHS) {
371
// Fast path - add the numbers with fixed width.
372
T Result;
373
if constexpr (needsAlloc<T>())
374
Result = S.allocAP<T>(LHS.bitWidth());
375
376
if (!OpFW(LHS, RHS, Bits, &Result)) {
377
S.Stk.push<T>(Result);
378
return true;
379
}
380
// If for some reason evaluation continues, use the truncated results.
381
S.Stk.push<T>(Result);
382
383
// Short-circuit fixed-points here since the error handling is easier.
384
if constexpr (std::is_same_v<T, FixedPoint>)
385
return handleFixedPointOverflow(S, OpPC, Result);
386
387
// Slow path - compute the result using another bit of precision.
388
APSInt Value = OpAP<APSInt>()(LHS.toAPSInt(Bits), RHS.toAPSInt(Bits));
389
390
// Report undefined behaviour, stopping if required.
391
if (S.checkingForUndefinedBehavior()) {
392
const Expr *E = S.Current->getExpr(OpPC);
393
QualType Type = E->getType();
394
SmallString<32> Trunc;
395
Value.trunc(Result.bitWidth())
396
.toString(Trunc, 10, Result.isSigned(), /*formatAsCLiteral=*/false,
397
/*UpperCase=*/true, /*InsertSeparators=*/true);
398
S.report(E->getExprLoc(), diag::warn_integer_constant_overflow)
399
<< Trunc << Type << E->getSourceRange();
400
}
401
402
if (!handleOverflow(S, OpPC, Value)) {
403
S.Stk.pop<T>();
404
return false;
405
}
406
return true;
407
}
408
409
template <PrimType Name, class T = typename PrimConv<Name>::T>
410
bool Add(InterpState &S, CodePtr OpPC) {
411
const T &RHS = S.Stk.pop<T>();
412
const T &LHS = S.Stk.pop<T>();
413
const unsigned Bits = RHS.bitWidth() + 1;
414
415
return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS);
416
}
417
418
static inline llvm::RoundingMode getRoundingMode(FPOptions FPO) {
419
auto RM = FPO.getRoundingMode();
420
if (RM == llvm::RoundingMode::Dynamic)
421
return llvm::RoundingMode::NearestTiesToEven;
422
return RM;
423
}
424
425
inline bool Addf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
426
const Floating &RHS = S.Stk.pop<Floating>();
427
const Floating &LHS = S.Stk.pop<Floating>();
428
429
FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
430
Floating Result = S.allocFloat(LHS.getSemantics());
431
auto Status = Floating::add(LHS, RHS, getRoundingMode(FPO), &Result);
432
S.Stk.push<Floating>(Result);
433
return CheckFloatResult(S, OpPC, Result, Status, FPO);
434
}
435
436
template <PrimType Name, class T = typename PrimConv<Name>::T>
437
bool Sub(InterpState &S, CodePtr OpPC) {
438
const T &RHS = S.Stk.pop<T>();
439
const T &LHS = S.Stk.pop<T>();
440
const unsigned Bits = RHS.bitWidth() + 1;
441
442
return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS);
443
}
444
445
inline bool Subf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
446
const Floating &RHS = S.Stk.pop<Floating>();
447
const Floating &LHS = S.Stk.pop<Floating>();
448
449
FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
450
Floating Result = S.allocFloat(LHS.getSemantics());
451
auto Status = Floating::sub(LHS, RHS, getRoundingMode(FPO), &Result);
452
S.Stk.push<Floating>(Result);
453
return CheckFloatResult(S, OpPC, Result, Status, FPO);
454
}
455
456
template <PrimType Name, class T = typename PrimConv<Name>::T>
457
bool Mul(InterpState &S, CodePtr OpPC) {
458
const T &RHS = S.Stk.pop<T>();
459
const T &LHS = S.Stk.pop<T>();
460
const unsigned Bits = RHS.bitWidth() * 2;
461
462
return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
463
}
464
465
inline bool Mulf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
466
const Floating &RHS = S.Stk.pop<Floating>();
467
const Floating &LHS = S.Stk.pop<Floating>();
468
469
FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
470
Floating Result = S.allocFloat(LHS.getSemantics());
471
472
auto Status = Floating::mul(LHS, RHS, getRoundingMode(FPO), &Result);
473
474
S.Stk.push<Floating>(Result);
475
return CheckFloatResult(S, OpPC, Result, Status, FPO);
476
}
477
478
template <PrimType Name, class T = typename PrimConv<Name>::T>
479
inline bool Mulc(InterpState &S, CodePtr OpPC) {
480
const Pointer &RHS = S.Stk.pop<Pointer>();
481
const Pointer &LHS = S.Stk.pop<Pointer>();
482
const Pointer &Result = S.Stk.peek<Pointer>();
483
484
if constexpr (std::is_same_v<T, Floating>) {
485
APFloat A = LHS.atIndex(0).deref<Floating>().getAPFloat();
486
APFloat B = LHS.atIndex(1).deref<Floating>().getAPFloat();
487
APFloat C = RHS.atIndex(0).deref<Floating>().getAPFloat();
488
APFloat D = RHS.atIndex(1).deref<Floating>().getAPFloat();
489
490
APFloat ResR(A.getSemantics());
491
APFloat ResI(A.getSemantics());
492
HandleComplexComplexMul(A, B, C, D, ResR, ResI);
493
494
// Copy into the result.
495
Floating RA = S.allocFloat(A.getSemantics());
496
RA.copy(ResR);
497
Result.atIndex(0).deref<Floating>() = RA; // Floating(ResR);
498
Result.atIndex(0).initialize();
499
500
Floating RI = S.allocFloat(A.getSemantics());
501
RI.copy(ResI);
502
Result.atIndex(1).deref<Floating>() = RI; // Floating(ResI);
503
Result.atIndex(1).initialize();
504
Result.initialize();
505
} else {
506
// Integer element type.
507
const T &LHSR = LHS.atIndex(0).deref<T>();
508
const T &LHSI = LHS.atIndex(1).deref<T>();
509
const T &RHSR = RHS.atIndex(0).deref<T>();
510
const T &RHSI = RHS.atIndex(1).deref<T>();
511
unsigned Bits = LHSR.bitWidth();
512
513
// real(Result) = (real(LHS) * real(RHS)) - (imag(LHS) * imag(RHS))
514
T A;
515
if (T::mul(LHSR, RHSR, Bits, &A))
516
return false;
517
T B;
518
if (T::mul(LHSI, RHSI, Bits, &B))
519
return false;
520
if (T::sub(A, B, Bits, &Result.atIndex(0).deref<T>()))
521
return false;
522
Result.atIndex(0).initialize();
523
524
// imag(Result) = (real(LHS) * imag(RHS)) + (imag(LHS) * real(RHS))
525
if (T::mul(LHSR, RHSI, Bits, &A))
526
return false;
527
if (T::mul(LHSI, RHSR, Bits, &B))
528
return false;
529
if (T::add(A, B, Bits, &Result.atIndex(1).deref<T>()))
530
return false;
531
Result.atIndex(1).initialize();
532
Result.initialize();
533
}
534
535
return true;
536
}
537
538
template <PrimType Name, class T = typename PrimConv<Name>::T>
539
inline bool Divc(InterpState &S, CodePtr OpPC) {
540
const Pointer &RHS = S.Stk.pop<Pointer>();
541
const Pointer &LHS = S.Stk.pop<Pointer>();
542
const Pointer &Result = S.Stk.peek<Pointer>();
543
544
if constexpr (std::is_same_v<T, Floating>) {
545
APFloat A = LHS.atIndex(0).deref<Floating>().getAPFloat();
546
APFloat B = LHS.atIndex(1).deref<Floating>().getAPFloat();
547
APFloat C = RHS.atIndex(0).deref<Floating>().getAPFloat();
548
APFloat D = RHS.atIndex(1).deref<Floating>().getAPFloat();
549
550
APFloat ResR(A.getSemantics());
551
APFloat ResI(A.getSemantics());
552
HandleComplexComplexDiv(A, B, C, D, ResR, ResI);
553
554
// Copy into the result.
555
Floating RA = S.allocFloat(A.getSemantics());
556
RA.copy(ResR);
557
Result.atIndex(0).deref<Floating>() = RA; // Floating(ResR);
558
Result.atIndex(0).initialize();
559
560
Floating RI = S.allocFloat(A.getSemantics());
561
RI.copy(ResI);
562
Result.atIndex(1).deref<Floating>() = RI; // Floating(ResI);
563
Result.atIndex(1).initialize();
564
565
Result.initialize();
566
} else {
567
// Integer element type.
568
const T &LHSR = LHS.atIndex(0).deref<T>();
569
const T &LHSI = LHS.atIndex(1).deref<T>();
570
const T &RHSR = RHS.atIndex(0).deref<T>();
571
const T &RHSI = RHS.atIndex(1).deref<T>();
572
unsigned Bits = LHSR.bitWidth();
573
const T Zero = T::from(0, Bits);
574
575
if (Compare(RHSR, Zero) == ComparisonCategoryResult::Equal &&
576
Compare(RHSI, Zero) == ComparisonCategoryResult::Equal) {
577
const SourceInfo &E = S.Current->getSource(OpPC);
578
S.FFDiag(E, diag::note_expr_divide_by_zero);
579
return false;
580
}
581
582
// Den = real(RHS)² + imag(RHS)²
583
T A, B;
584
if (T::mul(RHSR, RHSR, Bits, &A) || T::mul(RHSI, RHSI, Bits, &B)) {
585
// Ignore overflow here, because that's what the current interpeter does.
586
}
587
T Den;
588
if (T::add(A, B, Bits, &Den))
589
return false;
590
591
if (Compare(Den, Zero) == ComparisonCategoryResult::Equal) {
592
const SourceInfo &E = S.Current->getSource(OpPC);
593
S.FFDiag(E, diag::note_expr_divide_by_zero);
594
return false;
595
}
596
597
// real(Result) = ((real(LHS) * real(RHS)) + (imag(LHS) * imag(RHS))) / Den
598
T &ResultR = Result.atIndex(0).deref<T>();
599
T &ResultI = Result.atIndex(1).deref<T>();
600
601
if (T::mul(LHSR, RHSR, Bits, &A) || T::mul(LHSI, RHSI, Bits, &B))
602
return false;
603
if (T::add(A, B, Bits, &ResultR))
604
return false;
605
if (T::div(ResultR, Den, Bits, &ResultR))
606
return false;
607
Result.atIndex(0).initialize();
608
609
// imag(Result) = ((imag(LHS) * real(RHS)) - (real(LHS) * imag(RHS))) / Den
610
if (T::mul(LHSI, RHSR, Bits, &A) || T::mul(LHSR, RHSI, Bits, &B))
611
return false;
612
if (T::sub(A, B, Bits, &ResultI))
613
return false;
614
if (T::div(ResultI, Den, Bits, &ResultI))
615
return false;
616
Result.atIndex(1).initialize();
617
Result.initialize();
618
}
619
620
return true;
621
}
622
623
/// 1) Pops the RHS from the stack.
624
/// 2) Pops the LHS from the stack.
625
/// 3) Pushes 'LHS & RHS' on the stack
626
template <PrimType Name, class T = typename PrimConv<Name>::T>
627
bool BitAnd(InterpState &S, CodePtr OpPC) {
628
const T &RHS = S.Stk.pop<T>();
629
const T &LHS = S.Stk.pop<T>();
630
unsigned Bits = RHS.bitWidth();
631
632
T Result;
633
if constexpr (needsAlloc<T>())
634
Result = S.allocAP<T>(Bits);
635
636
if (!T::bitAnd(LHS, RHS, Bits, &Result)) {
637
S.Stk.push<T>(Result);
638
return true;
639
}
640
return false;
641
}
642
643
/// 1) Pops the RHS from the stack.
644
/// 2) Pops the LHS from the stack.
645
/// 3) Pushes 'LHS | RHS' on the stack
646
template <PrimType Name, class T = typename PrimConv<Name>::T>
647
bool BitOr(InterpState &S, CodePtr OpPC) {
648
const T &RHS = S.Stk.pop<T>();
649
const T &LHS = S.Stk.pop<T>();
650
unsigned Bits = RHS.bitWidth();
651
652
T Result;
653
if constexpr (needsAlloc<T>())
654
Result = S.allocAP<T>(Bits);
655
656
if (!T::bitOr(LHS, RHS, Bits, &Result)) {
657
S.Stk.push<T>(Result);
658
return true;
659
}
660
return false;
661
}
662
663
/// 1) Pops the RHS from the stack.
664
/// 2) Pops the LHS from the stack.
665
/// 3) Pushes 'LHS ^ RHS' on the stack
666
template <PrimType Name, class T = typename PrimConv<Name>::T>
667
bool BitXor(InterpState &S, CodePtr OpPC) {
668
const T &RHS = S.Stk.pop<T>();
669
const T &LHS = S.Stk.pop<T>();
670
671
unsigned Bits = RHS.bitWidth();
672
673
T Result;
674
if constexpr (needsAlloc<T>())
675
Result = S.allocAP<T>(Bits);
676
677
if (!T::bitXor(LHS, RHS, Bits, &Result)) {
678
S.Stk.push<T>(Result);
679
return true;
680
}
681
return false;
682
}
683
684
/// 1) Pops the RHS from the stack.
685
/// 2) Pops the LHS from the stack.
686
/// 3) Pushes 'LHS % RHS' on the stack (the remainder of dividing LHS by RHS).
687
template <PrimType Name, class T = typename PrimConv<Name>::T>
688
bool Rem(InterpState &S, CodePtr OpPC) {
689
const T &RHS = S.Stk.pop<T>();
690
const T &LHS = S.Stk.pop<T>();
691
const unsigned Bits = RHS.bitWidth() * 2;
692
693
if (!CheckDivRem(S, OpPC, LHS, RHS))
694
return false;
695
696
T Result;
697
if constexpr (needsAlloc<T>())
698
Result = S.allocAP<T>(LHS.bitWidth());
699
700
if (!T::rem(LHS, RHS, Bits, &Result)) {
701
S.Stk.push<T>(Result);
702
return true;
703
}
704
return false;
705
}
706
707
/// 1) Pops the RHS from the stack.
708
/// 2) Pops the LHS from the stack.
709
/// 3) Pushes 'LHS / RHS' on the stack
710
template <PrimType Name, class T = typename PrimConv<Name>::T>
711
bool Div(InterpState &S, CodePtr OpPC) {
712
const T &RHS = S.Stk.pop<T>();
713
const T &LHS = S.Stk.pop<T>();
714
const unsigned Bits = RHS.bitWidth() * 2;
715
716
if (!CheckDivRem(S, OpPC, LHS, RHS))
717
return false;
718
719
T Result;
720
if constexpr (needsAlloc<T>())
721
Result = S.allocAP<T>(LHS.bitWidth());
722
723
if (!T::div(LHS, RHS, Bits, &Result)) {
724
S.Stk.push<T>(Result);
725
return true;
726
}
727
728
if constexpr (std::is_same_v<T, FixedPoint>) {
729
if (handleFixedPointOverflow(S, OpPC, Result)) {
730
S.Stk.push<T>(Result);
731
return true;
732
}
733
}
734
return false;
735
}
736
737
inline bool Divf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
738
const Floating &RHS = S.Stk.pop<Floating>();
739
const Floating &LHS = S.Stk.pop<Floating>();
740
741
if (!CheckDivRem(S, OpPC, LHS, RHS))
742
return false;
743
744
FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
745
746
Floating Result = S.allocFloat(LHS.getSemantics());
747
auto Status = Floating::div(LHS, RHS, getRoundingMode(FPO), &Result);
748
749
S.Stk.push<Floating>(Result);
750
return CheckFloatResult(S, OpPC, Result, Status, FPO);
751
}
752
753
//===----------------------------------------------------------------------===//
754
// Inv
755
//===----------------------------------------------------------------------===//
756
757
inline bool Inv(InterpState &S, CodePtr OpPC) {
758
const auto &Val = S.Stk.pop<Boolean>();
759
S.Stk.push<Boolean>(!Val);
760
return true;
761
}
762
763
//===----------------------------------------------------------------------===//
764
// Neg
765
//===----------------------------------------------------------------------===//
766
767
template <PrimType Name, class T = typename PrimConv<Name>::T>
768
bool Neg(InterpState &S, CodePtr OpPC) {
769
const T &Value = S.Stk.pop<T>();
770
771
if constexpr (std::is_same_v<T, Floating>) {
772
T Result = S.allocFloat(Value.getSemantics());
773
774
if (!T::neg(Value, &Result)) {
775
S.Stk.push<T>(Result);
776
return true;
777
}
778
return false;
779
} else {
780
T Result;
781
if constexpr (needsAlloc<T>())
782
Result = S.allocAP<T>(Value.bitWidth());
783
784
if (!T::neg(Value, &Result)) {
785
S.Stk.push<T>(Result);
786
return true;
787
}
788
789
assert(isIntegralType(Name) &&
790
"don't expect other types to fail at constexpr negation");
791
S.Stk.push<T>(Result);
792
793
APSInt NegatedValue = -Value.toAPSInt(Value.bitWidth() + 1);
794
if (S.checkingForUndefinedBehavior()) {
795
const Expr *E = S.Current->getExpr(OpPC);
796
QualType Type = E->getType();
797
SmallString<32> Trunc;
798
NegatedValue.trunc(Result.bitWidth())
799
.toString(Trunc, 10, Result.isSigned(), /*formatAsCLiteral=*/false,
800
/*UpperCase=*/true, /*InsertSeparators=*/true);
801
S.report(E->getExprLoc(), diag::warn_integer_constant_overflow)
802
<< Trunc << Type << E->getSourceRange();
803
return true;
804
}
805
806
return handleOverflow(S, OpPC, NegatedValue);
807
}
808
}
809
810
enum class PushVal : bool {
811
No,
812
Yes,
813
};
814
enum class IncDecOp {
815
Inc,
816
Dec,
817
};
818
819
template <typename T, IncDecOp Op, PushVal DoPush>
820
bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
821
bool CanOverflow) {
822
assert(!Ptr.isDummy());
823
824
if (!S.inConstantContext()) {
825
if (isConstexprUnknown(Ptr))
826
return false;
827
}
828
829
if constexpr (std::is_same_v<T, Boolean>) {
830
if (!S.getLangOpts().CPlusPlus14)
831
return Invalid(S, OpPC);
832
}
833
834
const T &Value = Ptr.deref<T>();
835
T Result;
836
if constexpr (needsAlloc<T>())
837
Result = S.allocAP<T>(Value.bitWidth());
838
839
if constexpr (DoPush == PushVal::Yes)
840
S.Stk.push<T>(Value);
841
842
if constexpr (Op == IncDecOp::Inc) {
843
if (!T::increment(Value, &Result) || !CanOverflow) {
844
Ptr.deref<T>() = Result;
845
return true;
846
}
847
} else {
848
if (!T::decrement(Value, &Result) || !CanOverflow) {
849
Ptr.deref<T>() = Result;
850
return true;
851
}
852
}
853
assert(CanOverflow);
854
855
// Something went wrong with the previous operation. Compute the
856
// result with another bit of precision.
857
unsigned Bits = Value.bitWidth() + 1;
858
APSInt APResult;
859
if constexpr (Op == IncDecOp::Inc)
860
APResult = ++Value.toAPSInt(Bits);
861
else
862
APResult = --Value.toAPSInt(Bits);
863
864
// Report undefined behaviour, stopping if required.
865
if (S.checkingForUndefinedBehavior()) {
866
const Expr *E = S.Current->getExpr(OpPC);
867
QualType Type = E->getType();
868
SmallString<32> Trunc;
869
APResult.trunc(Result.bitWidth())
870
.toString(Trunc, 10, Result.isSigned(), /*formatAsCLiteral=*/false,
871
/*UpperCase=*/true, /*InsertSeparators=*/true);
872
S.report(E->getExprLoc(), diag::warn_integer_constant_overflow)
873
<< Trunc << Type << E->getSourceRange();
874
return true;
875
}
876
return handleOverflow(S, OpPC, APResult);
877
}
878
879
/// 1) Pops a pointer from the stack
880
/// 2) Load the value from the pointer
881
/// 3) Writes the value increased by one back to the pointer
882
/// 4) Pushes the original (pre-inc) value on the stack.
883
template <PrimType Name, class T = typename PrimConv<Name>::T>
884
bool Inc(InterpState &S, CodePtr OpPC, bool CanOverflow) {
885
const Pointer &Ptr = S.Stk.pop<Pointer>();
886
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
887
return false;
888
889
return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr,
890
CanOverflow);
891
}
892
893
/// 1) Pops a pointer from the stack
894
/// 2) Load the value from the pointer
895
/// 3) Writes the value increased by one back to the pointer
896
template <PrimType Name, class T = typename PrimConv<Name>::T>
897
bool IncPop(InterpState &S, CodePtr OpPC, bool CanOverflow) {
898
const Pointer &Ptr = S.Stk.pop<Pointer>();
899
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
900
return false;
901
902
return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow);
903
}
904
905
template <PrimType Name, class T = typename PrimConv<Name>::T>
906
bool PreInc(InterpState &S, CodePtr OpPC, bool CanOverflow) {
907
const Pointer &Ptr = S.Stk.peek<Pointer>();
908
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
909
return false;
910
911
return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow);
912
}
913
914
/// 1) Pops a pointer from the stack
915
/// 2) Load the value from the pointer
916
/// 3) Writes the value decreased by one back to the pointer
917
/// 4) Pushes the original (pre-dec) value on the stack.
918
template <PrimType Name, class T = typename PrimConv<Name>::T>
919
bool Dec(InterpState &S, CodePtr OpPC, bool CanOverflow) {
920
const Pointer &Ptr = S.Stk.pop<Pointer>();
921
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
922
return false;
923
924
return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr,
925
CanOverflow);
926
}
927
928
/// 1) Pops a pointer from the stack
929
/// 2) Load the value from the pointer
930
/// 3) Writes the value decreased by one back to the pointer
931
template <PrimType Name, class T = typename PrimConv<Name>::T>
932
bool DecPop(InterpState &S, CodePtr OpPC, bool CanOverflow) {
933
const Pointer &Ptr = S.Stk.pop<Pointer>();
934
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
935
return false;
936
937
return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow);
938
}
939
940
template <PrimType Name, class T = typename PrimConv<Name>::T>
941
bool PreDec(InterpState &S, CodePtr OpPC, bool CanOverflow) {
942
const Pointer &Ptr = S.Stk.peek<Pointer>();
943
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
944
return false;
945
return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow);
946
}
947
948
template <IncDecOp Op, PushVal DoPush>
949
bool IncDecFloatHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
950
uint32_t FPOI) {
951
Floating Value = Ptr.deref<Floating>();
952
Floating Result = S.allocFloat(Value.getSemantics());
953
954
if constexpr (DoPush == PushVal::Yes)
955
S.Stk.push<Floating>(Value);
956
957
FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
958
llvm::APFloat::opStatus Status;
959
if constexpr (Op == IncDecOp::Inc)
960
Status = Floating::increment(Value, getRoundingMode(FPO), &Result);
961
else
962
Status = Floating::decrement(Value, getRoundingMode(FPO), &Result);
963
964
Ptr.deref<Floating>() = Result;
965
966
return CheckFloatResult(S, OpPC, Result, Status, FPO);
967
}
968
969
inline bool Incf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
970
const Pointer &Ptr = S.Stk.pop<Pointer>();
971
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
972
return false;
973
974
return IncDecFloatHelper<IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr, FPOI);
975
}
976
977
inline bool IncfPop(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
978
const Pointer &Ptr = S.Stk.pop<Pointer>();
979
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
980
return false;
981
982
return IncDecFloatHelper<IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, FPOI);
983
}
984
985
inline bool Decf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
986
const Pointer &Ptr = S.Stk.pop<Pointer>();
987
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
988
return false;
989
990
return IncDecFloatHelper<IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr, FPOI);
991
}
992
993
inline bool DecfPop(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
994
const Pointer &Ptr = S.Stk.pop<Pointer>();
995
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
996
return false;
997
998
return IncDecFloatHelper<IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, FPOI);
999
}
1000
1001
/// 1) Pops the value from the stack.
1002
/// 2) Pushes the bitwise complemented value on the stack (~V).
1003
template <PrimType Name, class T = typename PrimConv<Name>::T>
1004
bool Comp(InterpState &S, CodePtr OpPC) {
1005
const T &Val = S.Stk.pop<T>();
1006
1007
T Result;
1008
if constexpr (needsAlloc<T>())
1009
Result = S.allocAP<T>(Val.bitWidth());
1010
1011
if (!T::comp(Val, &Result)) {
1012
S.Stk.push<T>(Result);
1013
return true;
1014
}
1015
return false;
1016
}
1017
1018
//===----------------------------------------------------------------------===//
1019
// EQ, NE, GT, GE, LT, LE
1020
//===----------------------------------------------------------------------===//
1021
1022
using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>;
1023
1024
template <typename T>
1025
bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) {
1026
assert((!std::is_same_v<T, MemberPointer>) &&
1027
"Non-equality comparisons on member pointer types should already be "
1028
"rejected in Sema.");
1029
using BoolT = PrimConv<PT_Bool>::T;
1030
const T &RHS = S.Stk.pop<T>();
1031
const T &LHS = S.Stk.pop<T>();
1032
S.Stk.push<BoolT>(BoolT::from(Fn(LHS.compare(RHS))));
1033
return true;
1034
}
1035
1036
template <typename T>
1037
bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) {
1038
return CmpHelper<T>(S, OpPC, Fn);
1039
}
1040
1041
template <>
1042
inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
1043
using BoolT = PrimConv<PT_Bool>::T;
1044
const Pointer &RHS = S.Stk.pop<Pointer>();
1045
const Pointer &LHS = S.Stk.pop<Pointer>();
1046
1047
// Function pointers cannot be compared in an ordered way.
1048
if (LHS.isFunctionPointer() || RHS.isFunctionPointer() ||
1049
LHS.isTypeidPointer() || RHS.isTypeidPointer()) {
1050
const SourceInfo &Loc = S.Current->getSource(OpPC);
1051
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified)
1052
<< LHS.toDiagnosticString(S.getASTContext())
1053
<< RHS.toDiagnosticString(S.getASTContext());
1054
return false;
1055
}
1056
1057
if (!Pointer::hasSameBase(LHS, RHS)) {
1058
const SourceInfo &Loc = S.Current->getSource(OpPC);
1059
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified)
1060
<< LHS.toDiagnosticString(S.getASTContext())
1061
<< RHS.toDiagnosticString(S.getASTContext());
1062
return false;
1063
}
1064
1065
// Diagnose comparisons between fields with different access specifiers.
1066
if (std::optional<std::pair<Pointer, Pointer>> Split =
1067
Pointer::computeSplitPoint(LHS, RHS)) {
1068
const FieldDecl *LF = Split->first.getField();
1069
const FieldDecl *RF = Split->second.getField();
1070
if (LF && RF && !LF->getParent()->isUnion() &&
1071
LF->getAccess() != RF->getAccess()) {
1072
S.CCEDiag(S.Current->getSource(OpPC),
1073
diag::note_constexpr_pointer_comparison_differing_access)
1074
<< LF << LF->getAccess() << RF << RF->getAccess() << LF->getParent();
1075
}
1076
}
1077
1078
unsigned VL = LHS.getByteOffset();
1079
unsigned VR = RHS.getByteOffset();
1080
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
1081
return true;
1082
}
1083
1084
static inline bool IsOpaqueConstantCall(const CallExpr *E) {
1085
unsigned Builtin = E->getBuiltinCallee();
1086
return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
1087
Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
1088
Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
1089
Builtin == Builtin::BI__builtin_function_start);
1090
}
1091
1092
bool arePotentiallyOverlappingStringLiterals(const Pointer &LHS,
1093
const Pointer &RHS);
1094
1095
template <>
1096
inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
1097
using BoolT = PrimConv<PT_Bool>::T;
1098
const Pointer &RHS = S.Stk.pop<Pointer>();
1099
const Pointer &LHS = S.Stk.pop<Pointer>();
1100
1101
if (LHS.isZero() && RHS.isZero()) {
1102
S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Equal)));
1103
return true;
1104
}
1105
1106
// Reject comparisons to weak pointers.
1107
for (const auto &P : {LHS, RHS}) {
1108
if (P.isZero())
1109
continue;
1110
if (P.isWeak()) {
1111
const SourceInfo &Loc = S.Current->getSource(OpPC);
1112
S.FFDiag(Loc, diag::note_constexpr_pointer_weak_comparison)
1113
<< P.toDiagnosticString(S.getASTContext());
1114
return false;
1115
}
1116
}
1117
1118
if (!S.inConstantContext()) {
1119
if (isConstexprUnknown(LHS) || isConstexprUnknown(RHS))
1120
return false;
1121
}
1122
1123
if (LHS.isFunctionPointer() && RHS.isFunctionPointer()) {
1124
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(LHS.getIntegerRepresentation(),
1125
RHS.getIntegerRepresentation()))));
1126
return true;
1127
}
1128
1129
// FIXME: The source check here isn't entirely correct.
1130
if (LHS.pointsToStringLiteral() && RHS.pointsToStringLiteral() &&
1131
LHS.getFieldDesc()->asExpr() != RHS.getFieldDesc()->asExpr()) {
1132
if (arePotentiallyOverlappingStringLiterals(LHS, RHS)) {
1133
const SourceInfo &Loc = S.Current->getSource(OpPC);
1134
S.FFDiag(Loc, diag::note_constexpr_literal_comparison)
1135
<< LHS.toDiagnosticString(S.getASTContext())
1136
<< RHS.toDiagnosticString(S.getASTContext());
1137
return false;
1138
}
1139
}
1140
1141
if (Pointer::hasSameBase(LHS, RHS)) {
1142
size_t A = LHS.computeOffsetForComparison();
1143
size_t B = RHS.computeOffsetForComparison();
1144
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(A, B))));
1145
return true;
1146
}
1147
1148
// Otherwise we need to do a bunch of extra checks before returning Unordered.
1149
if (LHS.isOnePastEnd() && !RHS.isOnePastEnd() && !RHS.isZero() &&
1150
RHS.getOffset() == 0) {
1151
const SourceInfo &Loc = S.Current->getSource(OpPC);
1152
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
1153
<< LHS.toDiagnosticString(S.getASTContext());
1154
return false;
1155
} else if (RHS.isOnePastEnd() && !LHS.isOnePastEnd() && !LHS.isZero() &&
1156
LHS.getOffset() == 0) {
1157
const SourceInfo &Loc = S.Current->getSource(OpPC);
1158
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
1159
<< RHS.toDiagnosticString(S.getASTContext());
1160
return false;
1161
}
1162
1163
bool BothNonNull = !LHS.isZero() && !RHS.isZero();
1164
// Reject comparisons to literals.
1165
for (const auto &P : {LHS, RHS}) {
1166
if (P.isZero())
1167
continue;
1168
if (BothNonNull && P.pointsToLiteral()) {
1169
const Expr *E = P.getDeclDesc()->asExpr();
1170
if (isa<StringLiteral>(E)) {
1171
const SourceInfo &Loc = S.Current->getSource(OpPC);
1172
S.FFDiag(Loc, diag::note_constexpr_literal_comparison);
1173
return false;
1174
} else if (const auto *CE = dyn_cast<CallExpr>(E);
1175
CE && IsOpaqueConstantCall(CE)) {
1176
const SourceInfo &Loc = S.Current->getSource(OpPC);
1177
S.FFDiag(Loc, diag::note_constexpr_opaque_call_comparison)
1178
<< P.toDiagnosticString(S.getASTContext());
1179
return false;
1180
}
1181
} else if (BothNonNull && P.isIntegralPointer()) {
1182
const SourceInfo &Loc = S.Current->getSource(OpPC);
1183
S.FFDiag(Loc, diag::note_constexpr_pointer_constant_comparison)
1184
<< LHS.toDiagnosticString(S.getASTContext())
1185
<< RHS.toDiagnosticString(S.getASTContext());
1186
return false;
1187
}
1188
}
1189
1190
if (LHS.isUnknownSizeArray() && RHS.isUnknownSizeArray()) {
1191
const SourceInfo &Loc = S.Current->getSource(OpPC);
1192
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_zero_sized)
1193
<< LHS.toDiagnosticString(S.getASTContext())
1194
<< RHS.toDiagnosticString(S.getASTContext());
1195
return false;
1196
}
1197
1198
S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered)));
1199
return true;
1200
}
1201
1202
template <>
1203
inline bool CmpHelperEQ<MemberPointer>(InterpState &S, CodePtr OpPC,
1204
CompareFn Fn) {
1205
const auto &RHS = S.Stk.pop<MemberPointer>();
1206
const auto &LHS = S.Stk.pop<MemberPointer>();
1207
1208
// If either operand is a pointer to a weak function, the comparison is not
1209
// constant.
1210
for (const auto &MP : {LHS, RHS}) {
1211
if (MP.isWeak()) {
1212
const SourceInfo &Loc = S.Current->getSource(OpPC);
1213
S.FFDiag(Loc, diag::note_constexpr_mem_pointer_weak_comparison)
1214
<< MP.getMemberFunction();
1215
return false;
1216
}
1217
}
1218
1219
// C++11 [expr.eq]p2:
1220
// If both operands are null, they compare equal. Otherwise if only one is
1221
// null, they compare unequal.
1222
if (LHS.isZero() && RHS.isZero()) {
1223
S.Stk.push<Boolean>(Fn(ComparisonCategoryResult::Equal));
1224
return true;
1225
}
1226
if (LHS.isZero() || RHS.isZero()) {
1227
S.Stk.push<Boolean>(Fn(ComparisonCategoryResult::Unordered));
1228
return true;
1229
}
1230
1231
// We cannot compare against virtual declarations at compile time.
1232
for (const auto &MP : {LHS, RHS}) {
1233
if (const CXXMethodDecl *MD = MP.getMemberFunction();
1234
MD && MD->isVirtual()) {
1235
const SourceInfo &Loc = S.Current->getSource(OpPC);
1236
S.CCEDiag(Loc, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
1237
}
1238
}
1239
1240
S.Stk.push<Boolean>(Boolean::from(Fn(LHS.compare(RHS))));
1241
return true;
1242
}
1243
1244
template <PrimType Name, class T = typename PrimConv<Name>::T>
1245
bool EQ(InterpState &S, CodePtr OpPC) {
1246
return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
1247
return R == ComparisonCategoryResult::Equal;
1248
});
1249
}
1250
1251
template <PrimType Name, class T = typename PrimConv<Name>::T>
1252
bool CMP3(InterpState &S, CodePtr OpPC, const ComparisonCategoryInfo *CmpInfo) {
1253
const T &RHS = S.Stk.pop<T>();
1254
const T &LHS = S.Stk.pop<T>();
1255
const Pointer &P = S.Stk.peek<Pointer>();
1256
1257
ComparisonCategoryResult CmpResult = LHS.compare(RHS);
1258
if constexpr (std::is_same_v<T, Pointer>) {
1259
if (CmpResult == ComparisonCategoryResult::Unordered) {
1260
const SourceInfo &Loc = S.Current->getSource(OpPC);
1261
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified)
1262
<< LHS.toDiagnosticString(S.getASTContext())
1263
<< RHS.toDiagnosticString(S.getASTContext());
1264
return false;
1265
}
1266
}
1267
1268
assert(CmpInfo);
1269
const auto *CmpValueInfo =
1270
CmpInfo->getValueInfo(CmpInfo->makeWeakResult(CmpResult));
1271
assert(CmpValueInfo);
1272
assert(CmpValueInfo->hasValidIntValue());
1273
return SetThreeWayComparisonField(S, OpPC, P, CmpValueInfo->getIntValue());
1274
}
1275
1276
template <PrimType Name, class T = typename PrimConv<Name>::T>
1277
bool NE(InterpState &S, CodePtr OpPC) {
1278
return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
1279
return R != ComparisonCategoryResult::Equal;
1280
});
1281
}
1282
1283
template <PrimType Name, class T = typename PrimConv<Name>::T>
1284
bool LT(InterpState &S, CodePtr OpPC) {
1285
return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
1286
return R == ComparisonCategoryResult::Less;
1287
});
1288
}
1289
1290
template <PrimType Name, class T = typename PrimConv<Name>::T>
1291
bool LE(InterpState &S, CodePtr OpPC) {
1292
return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
1293
return R == ComparisonCategoryResult::Less ||
1294
R == ComparisonCategoryResult::Equal;
1295
});
1296
}
1297
1298
template <PrimType Name, class T = typename PrimConv<Name>::T>
1299
bool GT(InterpState &S, CodePtr OpPC) {
1300
return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
1301
return R == ComparisonCategoryResult::Greater;
1302
});
1303
}
1304
1305
template <PrimType Name, class T = typename PrimConv<Name>::T>
1306
bool GE(InterpState &S, CodePtr OpPC) {
1307
return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
1308
return R == ComparisonCategoryResult::Greater ||
1309
R == ComparisonCategoryResult::Equal;
1310
});
1311
}
1312
1313
//===----------------------------------------------------------------------===//
1314
// Dup, Pop, Test
1315
//===----------------------------------------------------------------------===//
1316
1317
template <PrimType Name, class T = typename PrimConv<Name>::T>
1318
bool Dup(InterpState &S, CodePtr OpPC) {
1319
S.Stk.push<T>(S.Stk.peek<T>());
1320
return true;
1321
}
1322
1323
template <PrimType Name, class T = typename PrimConv<Name>::T>
1324
bool Pop(InterpState &S, CodePtr OpPC) {
1325
S.Stk.pop<T>();
1326
return true;
1327
}
1328
1329
/// [Value1, Value2] -> [Value2, Value1]
1330
template <PrimType TopName, PrimType BottomName>
1331
bool Flip(InterpState &S, CodePtr OpPC) {
1332
using TopT = typename PrimConv<TopName>::T;
1333
using BottomT = typename PrimConv<BottomName>::T;
1334
1335
const auto &Top = S.Stk.pop<TopT>();
1336
const auto &Bottom = S.Stk.pop<BottomT>();
1337
1338
S.Stk.push<TopT>(Top);
1339
S.Stk.push<BottomT>(Bottom);
1340
1341
return true;
1342
}
1343
1344
//===----------------------------------------------------------------------===//
1345
// Const
1346
//===----------------------------------------------------------------------===//
1347
1348
template <PrimType Name, class T = typename PrimConv<Name>::T>
1349
bool Const(InterpState &S, CodePtr OpPC, const T &Arg) {
1350
if constexpr (needsAlloc<T>()) {
1351
T Result = S.allocAP<T>(Arg.bitWidth());
1352
Result.copy(Arg.toAPSInt());
1353
S.Stk.push<T>(Result);
1354
return true;
1355
}
1356
S.Stk.push<T>(Arg);
1357
return true;
1358
}
1359
1360
inline bool ConstFloat(InterpState &S, CodePtr OpPC, const Floating &F) {
1361
Floating Result = S.allocFloat(F.getSemantics());
1362
Result.copy(F.getAPFloat());
1363
S.Stk.push<Floating>(Result);
1364
return true;
1365
}
1366
1367
//===----------------------------------------------------------------------===//
1368
// Get/Set Local/Param/Global/This
1369
//===----------------------------------------------------------------------===//
1370
1371
template <PrimType Name, class T = typename PrimConv<Name>::T>
1372
bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
1373
const Pointer &Ptr = S.Current->getLocalPointer(I);
1374
if (!CheckLoad(S, OpPC, Ptr))
1375
return false;
1376
S.Stk.push<T>(Ptr.deref<T>());
1377
return true;
1378
}
1379
1380
bool EndLifetime(InterpState &S, CodePtr OpPC);
1381
bool EndLifetimePop(InterpState &S, CodePtr OpPC);
1382
bool StartLifetime(InterpState &S, CodePtr OpPC);
1383
1384
/// 1) Pops the value from the stack.
1385
/// 2) Writes the value to the local variable with the
1386
/// given offset.
1387
template <PrimType Name, class T = typename PrimConv<Name>::T>
1388
bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
1389
S.Current->setLocal<T>(I, S.Stk.pop<T>());
1390
return true;
1391
}
1392
1393
template <PrimType Name, class T = typename PrimConv<Name>::T>
1394
bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
1395
if (S.checkingPotentialConstantExpression()) {
1396
return false;
1397
}
1398
S.Stk.push<T>(S.Current->getParam<T>(I));
1399
return true;
1400
}
1401
1402
template <PrimType Name, class T = typename PrimConv<Name>::T>
1403
bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
1404
S.Current->setParam<T>(I, S.Stk.pop<T>());
1405
return true;
1406
}
1407
1408
/// 1) Peeks a pointer on the stack
1409
/// 2) Pushes the value of the pointer's field on the stack
1410
template <PrimType Name, class T = typename PrimConv<Name>::T>
1411
bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
1412
const Pointer &Obj = S.Stk.peek<Pointer>();
1413
if (!CheckNull(S, OpPC, Obj, CSK_Field))
1414
return false;
1415
if (!CheckRange(S, OpPC, Obj, CSK_Field))
1416
return false;
1417
const Pointer &Field = Obj.atField(I);
1418
if (!CheckLoad(S, OpPC, Field))
1419
return false;
1420
S.Stk.push<T>(Field.deref<T>());
1421
return true;
1422
}
1423
1424
template <PrimType Name, class T = typename PrimConv<Name>::T>
1425
bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) {
1426
const T &Value = S.Stk.pop<T>();
1427
const Pointer &Obj = S.Stk.peek<Pointer>();
1428
if (!CheckNull(S, OpPC, Obj, CSK_Field))
1429
return false;
1430
if (!CheckRange(S, OpPC, Obj, CSK_Field))
1431
return false;
1432
const Pointer &Field = Obj.atField(I);
1433
if (!CheckStore(S, OpPC, Field))
1434
return false;
1435
Field.initialize();
1436
Field.deref<T>() = Value;
1437
return true;
1438
}
1439
1440
/// 1) Pops a pointer from the stack
1441
/// 2) Pushes the value of the pointer's field on the stack
1442
template <PrimType Name, class T = typename PrimConv<Name>::T>
1443
bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
1444
const Pointer &Obj = S.Stk.pop<Pointer>();
1445
if (!CheckNull(S, OpPC, Obj, CSK_Field))
1446
return false;
1447
if (!CheckRange(S, OpPC, Obj, CSK_Field))
1448
return false;
1449
const Pointer &Field = Obj.atField(I);
1450
if (!CheckLoad(S, OpPC, Field))
1451
return false;
1452
S.Stk.push<T>(Field.deref<T>());
1453
return true;
1454
}
1455
1456
template <PrimType Name, class T = typename PrimConv<Name>::T>
1457
bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
1458
if (S.checkingPotentialConstantExpression())
1459
return false;
1460
const Pointer &This = S.Current->getThis();
1461
if (!CheckThis(S, OpPC, This))
1462
return false;
1463
const Pointer &Field = This.atField(I);
1464
if (!CheckLoad(S, OpPC, Field))
1465
return false;
1466
S.Stk.push<T>(Field.deref<T>());
1467
return true;
1468
}
1469
1470
template <PrimType Name, class T = typename PrimConv<Name>::T>
1471
bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
1472
if (S.checkingPotentialConstantExpression())
1473
return false;
1474
const T &Value = S.Stk.pop<T>();
1475
const Pointer &This = S.Current->getThis();
1476
if (!CheckThis(S, OpPC, This))
1477
return false;
1478
const Pointer &Field = This.atField(I);
1479
if (!CheckStore(S, OpPC, Field))
1480
return false;
1481
Field.deref<T>() = Value;
1482
return true;
1483
}
1484
1485
template <PrimType Name, class T = typename PrimConv<Name>::T>
1486
bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
1487
const Pointer &Ptr = S.P.getPtrGlobal(I);
1488
if (!CheckConstant(S, OpPC, Ptr.getFieldDesc()))
1489
return false;
1490
if (Ptr.isExtern())
1491
return false;
1492
1493
// If a global variable is uninitialized, that means the initializer we've
1494
// compiled for it wasn't a constant expression. Diagnose that.
1495
if (!CheckGlobalInitialized(S, OpPC, Ptr))
1496
return false;
1497
1498
S.Stk.push<T>(Ptr.deref<T>());
1499
return true;
1500
}
1501
1502
/// Same as GetGlobal, but without the checks.
1503
template <PrimType Name, class T = typename PrimConv<Name>::T>
1504
bool GetGlobalUnchecked(InterpState &S, CodePtr OpPC, uint32_t I) {
1505
const Pointer &Ptr = S.P.getPtrGlobal(I);
1506
if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
1507
return false;
1508
S.Stk.push<T>(Ptr.deref<T>());
1509
return true;
1510
}
1511
1512
template <PrimType Name, class T = typename PrimConv<Name>::T>
1513
bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
1514
// TODO: emit warning.
1515
return false;
1516
}
1517
1518
template <PrimType Name, class T = typename PrimConv<Name>::T>
1519
bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
1520
const Pointer &P = S.P.getGlobal(I);
1521
1522
P.deref<T>() = S.Stk.pop<T>();
1523
1524
if constexpr (std::is_same_v<T, Floating>) {
1525
auto &Val = P.deref<Floating>();
1526
if (!Val.singleWord()) {
1527
uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
1528
Val.take(NewMemory);
1529
}
1530
1531
} else if constexpr (needsAlloc<T>()) {
1532
auto &Val = P.deref<T>();
1533
if (!Val.singleWord()) {
1534
uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
1535
Val.take(NewMemory);
1536
}
1537
}
1538
1539
P.initialize();
1540
return true;
1541
}
1542
1543
/// 1) Converts the value on top of the stack to an APValue
1544
/// 2) Sets that APValue on \Temp
1545
/// 3) Initializes global with index \I with that
1546
template <PrimType Name, class T = typename PrimConv<Name>::T>
1547
bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
1548
const LifetimeExtendedTemporaryDecl *Temp) {
1549
const Pointer &Ptr = S.P.getGlobal(I);
1550
1551
const T Value = S.Stk.peek<T>();
1552
APValue APV = Value.toAPValue(S.getASTContext());
1553
APValue *Cached = Temp->getOrCreateValue(true);
1554
*Cached = APV;
1555
1556
assert(Ptr.getDeclDesc()->asExpr());
1557
1558
S.SeenGlobalTemporaries.push_back(
1559
std::make_pair(Ptr.getDeclDesc()->asExpr(), Temp));
1560
1561
Ptr.deref<T>() = S.Stk.pop<T>();
1562
Ptr.initialize();
1563
return true;
1564
}
1565
1566
/// 1) Converts the value on top of the stack to an APValue
1567
/// 2) Sets that APValue on \Temp
1568
/// 3) Initialized global with index \I with that
1569
inline bool InitGlobalTempComp(InterpState &S, CodePtr OpPC,
1570
const LifetimeExtendedTemporaryDecl *Temp) {
1571
assert(Temp);
1572
const Pointer &P = S.Stk.peek<Pointer>();
1573
APValue *Cached = Temp->getOrCreateValue(true);
1574
1575
S.SeenGlobalTemporaries.push_back(
1576
std::make_pair(P.getDeclDesc()->asExpr(), Temp));
1577
1578
if (std::optional<APValue> APV =
1579
P.toRValue(S.getASTContext(), Temp->getTemporaryExpr()->getType())) {
1580
*Cached = *APV;
1581
return true;
1582
}
1583
1584
return false;
1585
}
1586
1587
template <PrimType Name, class T = typename PrimConv<Name>::T>
1588
bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
1589
if (S.checkingPotentialConstantExpression() && S.Current->getDepth() == 0)
1590
return false;
1591
const Pointer &This = S.Current->getThis();
1592
if (!CheckThis(S, OpPC, This))
1593
return false;
1594
const Pointer &Field = This.atField(I);
1595
Field.deref<T>() = S.Stk.pop<T>();
1596
Field.activate();
1597
Field.initialize();
1598
return true;
1599
}
1600
1601
// FIXME: The Field pointer here is too much IMO and we could instead just
1602
// pass an Offset + BitWidth pair.
1603
template <PrimType Name, class T = typename PrimConv<Name>::T>
1604
bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
1605
uint32_t FieldOffset) {
1606
assert(F->isBitField());
1607
if (S.checkingPotentialConstantExpression() && S.Current->getDepth() == 0)
1608
return false;
1609
const Pointer &This = S.Current->getThis();
1610
if (!CheckThis(S, OpPC, This))
1611
return false;
1612
const Pointer &Field = This.atField(FieldOffset);
1613
const auto &Value = S.Stk.pop<T>();
1614
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue());
1615
Field.initialize();
1616
return true;
1617
}
1618
1619
/// 1) Pops the value from the stack
1620
/// 2) Peeks a pointer from the stack
1621
/// 3) Pushes the value to field I of the pointer on the stack
1622
template <PrimType Name, class T = typename PrimConv<Name>::T>
1623
bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
1624
const T &Value = S.Stk.pop<T>();
1625
const Pointer &Ptr = S.Stk.peek<Pointer>();
1626
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
1627
return false;
1628
const Pointer &Field = Ptr.atField(I);
1629
Field.deref<T>() = Value;
1630
Field.activate();
1631
Field.initialize();
1632
return true;
1633
}
1634
1635
template <PrimType Name, class T = typename PrimConv<Name>::T>
1636
bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
1637
assert(F->isBitField());
1638
const T &Value = S.Stk.pop<T>();
1639
const Pointer &Field = S.Stk.peek<Pointer>().atField(F->Offset);
1640
1641
if constexpr (needsAlloc<T>()) {
1642
T Result = S.allocAP<T>(Value.bitWidth());
1643
if (T::isSigned())
1644
Result.copy(Value.toAPSInt()
1645
.trunc(F->Decl->getBitWidthValue())
1646
.sextOrTrunc(Value.bitWidth()));
1647
else
1648
Result.copy(Value.toAPSInt()
1649
.trunc(F->Decl->getBitWidthValue())
1650
.zextOrTrunc(Value.bitWidth()));
1651
1652
Field.deref<T>() = Result;
1653
} else {
1654
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue());
1655
}
1656
Field.activate();
1657
Field.initialize();
1658
return true;
1659
}
1660
1661
//===----------------------------------------------------------------------===//
1662
// GetPtr Local/Param/Global/Field/This
1663
//===----------------------------------------------------------------------===//
1664
1665
inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
1666
S.Stk.push<Pointer>(S.Current->getLocalPointer(I));
1667
return true;
1668
}
1669
1670
inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) {
1671
if (S.checkingPotentialConstantExpression()) {
1672
return false;
1673
}
1674
S.Stk.push<Pointer>(S.Current->getParamPointer(I));
1675
return true;
1676
}
1677
1678
inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
1679
S.Stk.push<Pointer>(S.P.getPtrGlobal(I));
1680
return true;
1681
}
1682
1683
/// 1) Peeks a Pointer
1684
/// 2) Pushes Pointer.atField(Off) on the stack
1685
bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off);
1686
bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off);
1687
1688
inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
1689
if (S.checkingPotentialConstantExpression() && S.Current->getDepth() == 0)
1690
return false;
1691
const Pointer &This = S.Current->getThis();
1692
if (!CheckThis(S, OpPC, This))
1693
return false;
1694
S.Stk.push<Pointer>(This.atField(Off));
1695
return true;
1696
}
1697
1698
inline bool GetPtrActiveField(InterpState &S, CodePtr OpPC, uint32_t Off) {
1699
const Pointer &Ptr = S.Stk.pop<Pointer>();
1700
if (!CheckNull(S, OpPC, Ptr, CSK_Field))
1701
return false;
1702
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
1703
return false;
1704
Pointer Field = Ptr.atField(Off);
1705
Ptr.deactivate();
1706
Field.activate();
1707
S.Stk.push<Pointer>(std::move(Field));
1708
return true;
1709
}
1710
1711
inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
1712
if (S.checkingPotentialConstantExpression())
1713
return false;
1714
const Pointer &This = S.Current->getThis();
1715
if (!CheckThis(S, OpPC, This))
1716
return false;
1717
Pointer Field = This.atField(Off);
1718
This.deactivate();
1719
Field.activate();
1720
S.Stk.push<Pointer>(std::move(Field));
1721
return true;
1722
}
1723
1724
inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off,
1725
bool NullOK, const Type *TargetType) {
1726
const Pointer &Ptr = S.Stk.pop<Pointer>();
1727
if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Derived))
1728
return false;
1729
1730
if (!Ptr.isBlockPointer()) {
1731
// FIXME: We don't have the necessary information in integral pointers.
1732
// The Descriptor only has a record, but that does of course not include
1733
// the potential derived classes of said record.
1734
S.Stk.push<Pointer>(Ptr);
1735
return true;
1736
}
1737
1738
if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
1739
return false;
1740
if (!CheckDowncast(S, OpPC, Ptr, Off))
1741
return false;
1742
1743
const Record *TargetRecord = Ptr.atFieldSub(Off).getRecord();
1744
assert(TargetRecord);
1745
1746
if (TargetRecord->getDecl()
1747
->getTypeForDecl()
1748
->getAsCXXRecordDecl()
1749
->getCanonicalDecl() !=
1750
TargetType->getAsCXXRecordDecl()->getCanonicalDecl()) {
1751
QualType MostDerivedType = Ptr.getDeclDesc()->getType();
1752
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_downcast)
1753
<< MostDerivedType << QualType(TargetType, 0);
1754
return false;
1755
}
1756
1757
S.Stk.push<Pointer>(Ptr.atFieldSub(Off));
1758
return true;
1759
}
1760
1761
inline bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
1762
const Pointer &Ptr = S.Stk.peek<Pointer>();
1763
if (!CheckNull(S, OpPC, Ptr, CSK_Base))
1764
return false;
1765
1766
if (!Ptr.isBlockPointer()) {
1767
S.Stk.push<Pointer>(Ptr.asIntPointer().baseCast(S.getASTContext(), Off));
1768
return true;
1769
}
1770
1771
if (!CheckSubobject(S, OpPC, Ptr, CSK_Base))
1772
return false;
1773
const Pointer &Result = Ptr.atField(Off);
1774
if (Result.isPastEnd() || !Result.isBaseClass())
1775
return false;
1776
S.Stk.push<Pointer>(Result);
1777
return true;
1778
}
1779
1780
inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off,
1781
bool NullOK) {
1782
const Pointer &Ptr = S.Stk.pop<Pointer>();
1783
1784
if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Base))
1785
return false;
1786
1787
if (!Ptr.isBlockPointer()) {
1788
S.Stk.push<Pointer>(Ptr.asIntPointer().baseCast(S.getASTContext(), Off));
1789
return true;
1790
}
1791
1792
if (!CheckSubobject(S, OpPC, Ptr, CSK_Base))
1793
return false;
1794
const Pointer &Result = Ptr.atField(Off);
1795
if (Result.isPastEnd() || !Result.isBaseClass())
1796
return false;
1797
S.Stk.push<Pointer>(Result);
1798
return true;
1799
}
1800
1801
inline bool GetMemberPtrBasePop(InterpState &S, CodePtr OpPC, int32_t Off) {
1802
const auto &Ptr = S.Stk.pop<MemberPointer>();
1803
S.Stk.push<MemberPointer>(Ptr.atInstanceBase(Off));
1804
return true;
1805
}
1806
1807
inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
1808
if (S.checkingPotentialConstantExpression())
1809
return false;
1810
const Pointer &This = S.Current->getThis();
1811
if (!CheckThis(S, OpPC, This))
1812
return false;
1813
S.Stk.push<Pointer>(This.atField(Off));
1814
return true;
1815
}
1816
1817
inline bool FinishInitPop(InterpState &S, CodePtr OpPC) {
1818
const Pointer &Ptr = S.Stk.pop<Pointer>();
1819
if (Ptr.canBeInitialized()) {
1820
Ptr.initialize();
1821
Ptr.activate();
1822
}
1823
return true;
1824
}
1825
1826
inline bool FinishInit(InterpState &S, CodePtr OpPC) {
1827
const Pointer &Ptr = S.Stk.peek<Pointer>();
1828
if (Ptr.canBeInitialized()) {
1829
Ptr.initialize();
1830
Ptr.activate();
1831
}
1832
return true;
1833
}
1834
1835
bool FinishInitGlobal(InterpState &S, CodePtr OpPC);
1836
1837
inline bool Dump(InterpState &S, CodePtr OpPC) {
1838
S.Stk.dump();
1839
return true;
1840
}
1841
1842
inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl,
1843
const Pointer &Ptr) {
1844
Pointer Base = Ptr;
1845
while (Base.isBaseClass())
1846
Base = Base.getBase();
1847
1848
const Record::Base *VirtBase = Base.getRecord()->getVirtualBase(Decl);
1849
S.Stk.push<Pointer>(Base.atField(VirtBase->Offset));
1850
return true;
1851
}
1852
1853
inline bool GetPtrVirtBasePop(InterpState &S, CodePtr OpPC,
1854
const RecordDecl *D) {
1855
assert(D);
1856
const Pointer &Ptr = S.Stk.pop<Pointer>();
1857
if (!CheckNull(S, OpPC, Ptr, CSK_Base))
1858
return false;
1859
return VirtBaseHelper(S, OpPC, D, Ptr);
1860
}
1861
1862
inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC,
1863
const RecordDecl *D) {
1864
assert(D);
1865
if (S.checkingPotentialConstantExpression())
1866
return false;
1867
const Pointer &This = S.Current->getThis();
1868
if (!CheckThis(S, OpPC, This))
1869
return false;
1870
return VirtBaseHelper(S, OpPC, D, S.Current->getThis());
1871
}
1872
1873
//===----------------------------------------------------------------------===//
1874
// Load, Store, Init
1875
//===----------------------------------------------------------------------===//
1876
1877
template <PrimType Name, class T = typename PrimConv<Name>::T>
1878
bool Load(InterpState &S, CodePtr OpPC) {
1879
const Pointer &Ptr = S.Stk.peek<Pointer>();
1880
if (!CheckLoad(S, OpPC, Ptr))
1881
return false;
1882
if (!Ptr.isBlockPointer())
1883
return false;
1884
S.Stk.push<T>(Ptr.deref<T>());
1885
return true;
1886
}
1887
1888
template <PrimType Name, class T = typename PrimConv<Name>::T>
1889
bool LoadPop(InterpState &S, CodePtr OpPC) {
1890
const Pointer &Ptr = S.Stk.pop<Pointer>();
1891
if (!CheckLoad(S, OpPC, Ptr))
1892
return false;
1893
if (!Ptr.isBlockPointer())
1894
return false;
1895
S.Stk.push<T>(Ptr.deref<T>());
1896
return true;
1897
}
1898
1899
template <PrimType Name, class T = typename PrimConv<Name>::T>
1900
bool Store(InterpState &S, CodePtr OpPC) {
1901
const T &Value = S.Stk.pop<T>();
1902
const Pointer &Ptr = S.Stk.peek<Pointer>();
1903
if (!CheckStore(S, OpPC, Ptr))
1904
return false;
1905
if (Ptr.canBeInitialized()) {
1906
Ptr.initialize();
1907
Ptr.activate();
1908
}
1909
Ptr.deref<T>() = Value;
1910
return true;
1911
}
1912
1913
template <PrimType Name, class T = typename PrimConv<Name>::T>
1914
bool StorePop(InterpState &S, CodePtr OpPC) {
1915
const T &Value = S.Stk.pop<T>();
1916
const Pointer &Ptr = S.Stk.pop<Pointer>();
1917
if (!CheckStore(S, OpPC, Ptr))
1918
return false;
1919
if (Ptr.canBeInitialized()) {
1920
Ptr.initialize();
1921
Ptr.activate();
1922
}
1923
Ptr.deref<T>() = Value;
1924
return true;
1925
}
1926
1927
template <PrimType Name, class T = typename PrimConv<Name>::T>
1928
bool StoreBitField(InterpState &S, CodePtr OpPC) {
1929
const T &Value = S.Stk.pop<T>();
1930
const Pointer &Ptr = S.Stk.peek<Pointer>();
1931
if (!CheckStore(S, OpPC, Ptr))
1932
return false;
1933
if (Ptr.canBeInitialized()) {
1934
Ptr.initialize();
1935
Ptr.activate();
1936
}
1937
if (const auto *FD = Ptr.getField())
1938
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
1939
else
1940
Ptr.deref<T>() = Value;
1941
return true;
1942
}
1943
1944
template <PrimType Name, class T = typename PrimConv<Name>::T>
1945
bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
1946
const T &Value = S.Stk.pop<T>();
1947
const Pointer &Ptr = S.Stk.pop<Pointer>();
1948
if (!CheckStore(S, OpPC, Ptr))
1949
return false;
1950
if (Ptr.canBeInitialized()) {
1951
Ptr.initialize();
1952
Ptr.activate();
1953
}
1954
if (const auto *FD = Ptr.getField())
1955
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
1956
else
1957
Ptr.deref<T>() = Value;
1958
return true;
1959
}
1960
1961
template <PrimType Name, class T = typename PrimConv<Name>::T>
1962
bool Init(InterpState &S, CodePtr OpPC) {
1963
const T &Value = S.Stk.pop<T>();
1964
const Pointer &Ptr = S.Stk.peek<Pointer>();
1965
if (!CheckInit(S, OpPC, Ptr))
1966
return false;
1967
Ptr.activate();
1968
Ptr.initialize();
1969
new (&Ptr.deref<T>()) T(Value);
1970
return true;
1971
}
1972
1973
template <PrimType Name, class T = typename PrimConv<Name>::T>
1974
bool InitPop(InterpState &S, CodePtr OpPC) {
1975
const T &Value = S.Stk.pop<T>();
1976
const Pointer &Ptr = S.Stk.pop<Pointer>();
1977
if (!CheckInit(S, OpPC, Ptr))
1978
return false;
1979
Ptr.activate();
1980
Ptr.initialize();
1981
new (&Ptr.deref<T>()) T(Value);
1982
return true;
1983
}
1984
1985
/// 1) Pops the value from the stack
1986
/// 2) Peeks a pointer and gets its index \Idx
1987
/// 3) Sets the value on the pointer, leaving the pointer on the stack.
1988
template <PrimType Name, class T = typename PrimConv<Name>::T>
1989
bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
1990
const T &Value = S.Stk.pop<T>();
1991
const Pointer &Ptr = S.Stk.peek<Pointer>();
1992
1993
if (Ptr.isUnknownSizeArray())
1994
return false;
1995
1996
// In the unlikely event that we're initializing the first item of
1997
// a non-array, skip the atIndex().
1998
if (Idx == 0 && !Ptr.getFieldDesc()->isArray()) {
1999
Ptr.initialize();
2000
new (&Ptr.deref<T>()) T(Value);
2001
return true;
2002
}
2003
2004
const Pointer &ElemPtr = Ptr.atIndex(Idx);
2005
if (!CheckInit(S, OpPC, ElemPtr))
2006
return false;
2007
ElemPtr.initialize();
2008
new (&ElemPtr.deref<T>()) T(Value);
2009
return true;
2010
}
2011
2012
/// The same as InitElem, but pops the pointer as well.
2013
template <PrimType Name, class T = typename PrimConv<Name>::T>
2014
bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
2015
const T &Value = S.Stk.pop<T>();
2016
const Pointer &Ptr = S.Stk.pop<Pointer>();
2017
if (Ptr.isUnknownSizeArray())
2018
return false;
2019
2020
// In the unlikely event that we're initializing the first item of
2021
// a non-array, skip the atIndex().
2022
if (Idx == 0 && !Ptr.getFieldDesc()->isArray()) {
2023
Ptr.initialize();
2024
new (&Ptr.deref<T>()) T(Value);
2025
return true;
2026
}
2027
2028
const Pointer &ElemPtr = Ptr.atIndex(Idx);
2029
if (!CheckInit(S, OpPC, ElemPtr))
2030
return false;
2031
ElemPtr.initialize();
2032
new (&ElemPtr.deref<T>()) T(Value);
2033
return true;
2034
}
2035
2036
inline bool Memcpy(InterpState &S, CodePtr OpPC) {
2037
const Pointer &Src = S.Stk.pop<Pointer>();
2038
Pointer &Dest = S.Stk.peek<Pointer>();
2039
2040
if (!CheckLoad(S, OpPC, Src))
2041
return false;
2042
2043
return DoMemcpy(S, OpPC, Src, Dest);
2044
}
2045
2046
inline bool ToMemberPtr(InterpState &S, CodePtr OpPC) {
2047
const auto &Member = S.Stk.pop<MemberPointer>();
2048
const auto &Base = S.Stk.pop<Pointer>();
2049
2050
S.Stk.push<MemberPointer>(Member.takeInstance(Base));
2051
return true;
2052
}
2053
2054
inline bool CastMemberPtrPtr(InterpState &S, CodePtr OpPC) {
2055
const auto &MP = S.Stk.pop<MemberPointer>();
2056
2057
if (std::optional<Pointer> Ptr = MP.toPointer(S.Ctx)) {
2058
S.Stk.push<Pointer>(*Ptr);
2059
return true;
2060
}
2061
return Invalid(S, OpPC);
2062
}
2063
2064
//===----------------------------------------------------------------------===//
2065
// AddOffset, SubOffset
2066
//===----------------------------------------------------------------------===//
2067
2068
template <class T, ArithOp Op>
2069
std::optional<Pointer> OffsetHelper(InterpState &S, CodePtr OpPC,
2070
const T &Offset, const Pointer &Ptr,
2071
bool IsPointerArith = false) {
2072
// A zero offset does not change the pointer.
2073
if (Offset.isZero())
2074
return Ptr;
2075
2076
if (IsPointerArith && !CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) {
2077
// The CheckNull will have emitted a note already, but we only
2078
// abort in C++, since this is fine in C.
2079
if (S.getLangOpts().CPlusPlus)
2080
return std::nullopt;
2081
}
2082
2083
// Arrays of unknown bounds cannot have pointers into them.
2084
if (!CheckArray(S, OpPC, Ptr))
2085
return std::nullopt;
2086
2087
// This is much simpler for integral pointers, so handle them first.
2088
if (Ptr.isIntegralPointer()) {
2089
uint64_t V = Ptr.getIntegerRepresentation();
2090
uint64_t O = static_cast<uint64_t>(Offset) * Ptr.elemSize();
2091
if constexpr (Op == ArithOp::Add)
2092
return Pointer(V + O, Ptr.asIntPointer().Desc);
2093
else
2094
return Pointer(V - O, Ptr.asIntPointer().Desc);
2095
} else if (Ptr.isFunctionPointer()) {
2096
uint64_t O = static_cast<uint64_t>(Offset);
2097
uint64_t N;
2098
if constexpr (Op == ArithOp::Add)
2099
N = Ptr.getByteOffset() + O;
2100
else
2101
N = Ptr.getByteOffset() - O;
2102
2103
if (N > 1)
2104
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
2105
<< N << /*non-array*/ true << 0;
2106
return Pointer(Ptr.asFunctionPointer().getFunction(), N);
2107
}
2108
2109
assert(Ptr.isBlockPointer());
2110
2111
uint64_t MaxIndex = static_cast<uint64_t>(Ptr.getNumElems());
2112
uint64_t Index;
2113
if (Ptr.isOnePastEnd())
2114
Index = MaxIndex;
2115
else
2116
Index = Ptr.getIndex();
2117
2118
bool Invalid = false;
2119
// Helper to report an invalid offset, computed as APSInt.
2120
auto DiagInvalidOffset = [&]() -> void {
2121
const unsigned Bits = Offset.bitWidth();
2122
APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), /*IsUnsigend=*/false);
2123
APSInt APIndex(APInt(Bits + 2, Index, /*IsSigned=*/true),
2124
/*IsUnsigned=*/false);
2125
APSInt NewIndex =
2126
(Op == ArithOp::Add) ? (APIndex + APOffset) : (APIndex - APOffset);
2127
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
2128
<< NewIndex << /*array*/ static_cast<int>(!Ptr.inArray()) << MaxIndex;
2129
Invalid = true;
2130
};
2131
2132
if (Ptr.isBlockPointer()) {
2133
uint64_t IOffset = static_cast<uint64_t>(Offset);
2134
uint64_t MaxOffset = MaxIndex - Index;
2135
2136
if constexpr (Op == ArithOp::Add) {
2137
// If the new offset would be negative, bail out.
2138
if (Offset.isNegative() && (Offset.isMin() || -IOffset > Index))
2139
DiagInvalidOffset();
2140
2141
// If the new offset would be out of bounds, bail out.
2142
if (Offset.isPositive() && IOffset > MaxOffset)
2143
DiagInvalidOffset();
2144
} else {
2145
// If the new offset would be negative, bail out.
2146
if (Offset.isPositive() && Index < IOffset)
2147
DiagInvalidOffset();
2148
2149
// If the new offset would be out of bounds, bail out.
2150
if (Offset.isNegative() && (Offset.isMin() || -IOffset > MaxOffset))
2151
DiagInvalidOffset();
2152
}
2153
}
2154
2155
if (Invalid && S.getLangOpts().CPlusPlus)
2156
return std::nullopt;
2157
2158
// Offset is valid - compute it on unsigned.
2159
int64_t WideIndex = static_cast<int64_t>(Index);
2160
int64_t WideOffset = static_cast<int64_t>(Offset);
2161
int64_t Result;
2162
if constexpr (Op == ArithOp::Add)
2163
Result = WideIndex + WideOffset;
2164
else
2165
Result = WideIndex - WideOffset;
2166
2167
// When the pointer is one-past-end, going back to index 0 is the only
2168
// useful thing we can do. Any other index has been diagnosed before and
2169
// we don't get here.
2170
if (Result == 0 && Ptr.isOnePastEnd()) {
2171
if (Ptr.getFieldDesc()->isArray())
2172
return Ptr.atIndex(0);
2173
return Pointer(Ptr.asBlockPointer().Pointee, Ptr.asBlockPointer().Base);
2174
}
2175
2176
return Ptr.atIndex(static_cast<uint64_t>(Result));
2177
}
2178
2179
template <PrimType Name, class T = typename PrimConv<Name>::T>
2180
bool AddOffset(InterpState &S, CodePtr OpPC) {
2181
const T &Offset = S.Stk.pop<T>();
2182
Pointer Ptr = S.Stk.pop<Pointer>();
2183
if (Ptr.isBlockPointer())
2184
Ptr = Ptr.expand();
2185
2186
if (std::optional<Pointer> Result = OffsetHelper<T, ArithOp::Add>(
2187
S, OpPC, Offset, Ptr, /*IsPointerArith=*/true)) {
2188
S.Stk.push<Pointer>(*Result);
2189
return true;
2190
}
2191
return false;
2192
}
2193
2194
template <PrimType Name, class T = typename PrimConv<Name>::T>
2195
bool SubOffset(InterpState &S, CodePtr OpPC) {
2196
const T &Offset = S.Stk.pop<T>();
2197
const Pointer &Ptr = S.Stk.pop<Pointer>();
2198
2199
if (std::optional<Pointer> Result = OffsetHelper<T, ArithOp::Sub>(
2200
S, OpPC, Offset, Ptr, /*IsPointerArith=*/true)) {
2201
S.Stk.push<Pointer>(*Result);
2202
return true;
2203
}
2204
return false;
2205
}
2206
2207
template <ArithOp Op>
2208
static inline bool IncDecPtrHelper(InterpState &S, CodePtr OpPC,
2209
const Pointer &Ptr) {
2210
if (Ptr.isDummy())
2211
return false;
2212
2213
using OneT = Integral<8, false>;
2214
2215
const Pointer &P = Ptr.deref<Pointer>();
2216
if (!CheckNull(S, OpPC, P, CSK_ArrayIndex))
2217
return false;
2218
2219
// Get the current value on the stack.
2220
S.Stk.push<Pointer>(P);
2221
2222
// Now the current Ptr again and a constant 1.
2223
OneT One = OneT::from(1);
2224
if (std::optional<Pointer> Result =
2225
OffsetHelper<OneT, Op>(S, OpPC, One, P, /*IsPointerArith=*/true)) {
2226
// Store the new value.
2227
Ptr.deref<Pointer>() = *Result;
2228
return true;
2229
}
2230
return false;
2231
}
2232
2233
static inline bool IncPtr(InterpState &S, CodePtr OpPC) {
2234
const Pointer &Ptr = S.Stk.pop<Pointer>();
2235
2236
if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
2237
return false;
2238
2239
return IncDecPtrHelper<ArithOp::Add>(S, OpPC, Ptr);
2240
}
2241
2242
static inline bool DecPtr(InterpState &S, CodePtr OpPC) {
2243
const Pointer &Ptr = S.Stk.pop<Pointer>();
2244
2245
if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
2246
return false;
2247
2248
return IncDecPtrHelper<ArithOp::Sub>(S, OpPC, Ptr);
2249
}
2250
2251
/// 1) Pops a Pointer from the stack.
2252
/// 2) Pops another Pointer from the stack.
2253
/// 3) Pushes the difference of the indices of the two pointers on the stack.
2254
template <PrimType Name, class T = typename PrimConv<Name>::T>
2255
inline bool SubPtr(InterpState &S, CodePtr OpPC) {
2256
const Pointer &LHS = S.Stk.pop<Pointer>();
2257
const Pointer &RHS = S.Stk.pop<Pointer>();
2258
2259
if (!Pointer::hasSameBase(LHS, RHS) && S.getLangOpts().CPlusPlus) {
2260
S.FFDiag(S.Current->getSource(OpPC),
2261
diag::note_constexpr_pointer_arith_unspecified)
2262
<< LHS.toDiagnosticString(S.getASTContext())
2263
<< RHS.toDiagnosticString(S.getASTContext());
2264
return false;
2265
}
2266
2267
if (LHS == RHS) {
2268
S.Stk.push<T>();
2269
return true;
2270
}
2271
2272
for (const Pointer &P : {LHS, RHS}) {
2273
if (P.isZeroSizeArray()) {
2274
QualType PtrT = P.getType();
2275
while (auto *AT = dyn_cast<ArrayType>(PtrT))
2276
PtrT = AT->getElementType();
2277
2278
QualType ArrayTy = S.getASTContext().getConstantArrayType(
2279
PtrT, APInt::getZero(1), nullptr, ArraySizeModifier::Normal, 0);
2280
S.FFDiag(S.Current->getSource(OpPC),
2281
diag::note_constexpr_pointer_subtraction_zero_size)
2282
<< ArrayTy;
2283
2284
return false;
2285
}
2286
}
2287
2288
int64_t A64 =
2289
LHS.isBlockPointer()
2290
? (LHS.isElementPastEnd() ? LHS.getNumElems() : LHS.getIndex())
2291
: LHS.getIntegerRepresentation();
2292
2293
int64_t B64 =
2294
RHS.isBlockPointer()
2295
? (RHS.isElementPastEnd() ? RHS.getNumElems() : RHS.getIndex())
2296
: RHS.getIntegerRepresentation();
2297
2298
int64_t R64 = A64 - B64;
2299
if (static_cast<int64_t>(T::from(R64)) != R64)
2300
return handleOverflow(S, OpPC, R64);
2301
2302
S.Stk.push<T>(T::from(R64));
2303
return true;
2304
}
2305
2306
//===----------------------------------------------------------------------===//
2307
// Destroy
2308
//===----------------------------------------------------------------------===//
2309
2310
inline bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) {
2311
assert(S.Current->getFunction());
2312
2313
// FIXME: We iterate the scope once here and then again in the destroy() call
2314
// below.
2315
for (auto &Local : S.Current->getFunction()->getScope(I).locals_reverse()) {
2316
const Pointer &Ptr = S.Current->getLocalPointer(Local.Offset);
2317
2318
if (Ptr.getLifetime() == Lifetime::Ended) {
2319
auto *D = cast<NamedDecl>(Ptr.getFieldDesc()->asDecl());
2320
S.FFDiag(D->getLocation(), diag::note_constexpr_destroy_out_of_lifetime)
2321
<< D->getNameAsString();
2322
return false;
2323
}
2324
}
2325
2326
S.Current->destroy(I);
2327
return true;
2328
}
2329
2330
inline bool InitScope(InterpState &S, CodePtr OpPC, uint32_t I) {
2331
S.Current->initScope(I);
2332
return true;
2333
}
2334
2335
//===----------------------------------------------------------------------===//
2336
// Cast, CastFP
2337
//===----------------------------------------------------------------------===//
2338
2339
template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) {
2340
using T = typename PrimConv<TIn>::T;
2341
using U = typename PrimConv<TOut>::T;
2342
S.Stk.push<U>(U::from(S.Stk.pop<T>()));
2343
return true;
2344
}
2345
2346
/// 1) Pops a Floating from the stack.
2347
/// 2) Pushes a new floating on the stack that uses the given semantics.
2348
inline bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem,
2349
llvm::RoundingMode RM) {
2350
Floating F = S.Stk.pop<Floating>();
2351
Floating Result = S.allocFloat(*Sem);
2352
F.toSemantics(Sem, RM, &Result);
2353
S.Stk.push<Floating>(Result);
2354
return true;
2355
}
2356
2357
inline bool CastFixedPoint(InterpState &S, CodePtr OpPC, uint32_t FPS) {
2358
FixedPointSemantics TargetSemantics =
2359
FixedPointSemantics::getFromOpaqueInt(FPS);
2360
const auto &Source = S.Stk.pop<FixedPoint>();
2361
2362
bool Overflow;
2363
FixedPoint Result = Source.toSemantics(TargetSemantics, &Overflow);
2364
2365
if (Overflow && !handleFixedPointOverflow(S, OpPC, Result))
2366
return false;
2367
2368
S.Stk.push<FixedPoint>(Result);
2369
return true;
2370
}
2371
2372
/// Like Cast(), but we cast to an arbitrary-bitwidth integral, so we need
2373
/// to know what bitwidth the result should be.
2374
template <PrimType Name, class T = typename PrimConv<Name>::T>
2375
bool CastAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
2376
auto Result = S.allocAP<IntegralAP<false>>(BitWidth);
2377
// Copy data.
2378
{
2379
APInt Source = S.Stk.pop<T>().toAPSInt().extOrTrunc(BitWidth);
2380
Result.copy(Source);
2381
}
2382
S.Stk.push<IntegralAP<false>>(Result);
2383
return true;
2384
}
2385
2386
template <PrimType Name, class T = typename PrimConv<Name>::T>
2387
bool CastAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
2388
auto Result = S.allocAP<IntegralAP<true>>(BitWidth);
2389
// Copy data.
2390
{
2391
APInt Source = S.Stk.pop<T>().toAPSInt().extOrTrunc(BitWidth);
2392
Result.copy(Source);
2393
}
2394
S.Stk.push<IntegralAP<true>>(Result);
2395
return true;
2396
}
2397
2398
template <PrimType Name, class T = typename PrimConv<Name>::T>
2399
bool CastIntegralFloating(InterpState &S, CodePtr OpPC,
2400
const llvm::fltSemantics *Sem, uint32_t FPOI) {
2401
const T &From = S.Stk.pop<T>();
2402
APSInt FromAP = From.toAPSInt();
2403
2404
FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
2405
Floating Result = S.allocFloat(*Sem);
2406
auto Status =
2407
Floating::fromIntegral(FromAP, *Sem, getRoundingMode(FPO), &Result);
2408
S.Stk.push<Floating>(Result);
2409
2410
return CheckFloatResult(S, OpPC, Result, Status, FPO);
2411
}
2412
2413
template <PrimType Name, class T = typename PrimConv<Name>::T>
2414
bool CastFloatingIntegral(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
2415
const Floating &F = S.Stk.pop<Floating>();
2416
2417
if constexpr (std::is_same_v<T, Boolean>) {
2418
S.Stk.push<T>(T(F.isNonZero()));
2419
return true;
2420
} else {
2421
APSInt Result(std::max(8u, T::bitWidth()),
2422
/*IsUnsigned=*/!T::isSigned());
2423
auto Status = F.convertToInteger(Result);
2424
2425
// Float-to-Integral overflow check.
2426
if ((Status & APFloat::opStatus::opInvalidOp)) {
2427
const Expr *E = S.Current->getExpr(OpPC);
2428
QualType Type = E->getType();
2429
2430
S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
2431
if (S.noteUndefinedBehavior()) {
2432
S.Stk.push<T>(T(Result));
2433
return true;
2434
}
2435
return false;
2436
}
2437
2438
FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
2439
S.Stk.push<T>(T(Result));
2440
return CheckFloatResult(S, OpPC, F, Status, FPO);
2441
}
2442
}
2443
2444
static inline bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC,
2445
uint32_t BitWidth, uint32_t FPOI) {
2446
const Floating &F = S.Stk.pop<Floating>();
2447
2448
APSInt Result(BitWidth, /*IsUnsigned=*/true);
2449
auto Status = F.convertToInteger(Result);
2450
2451
// Float-to-Integral overflow check.
2452
if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite())
2453
return handleOverflow(S, OpPC, F.getAPFloat());
2454
2455
FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
2456
2457
auto ResultAP = S.allocAP<IntegralAP<false>>(BitWidth);
2458
ResultAP.copy(Result);
2459
2460
S.Stk.push<IntegralAP<false>>(ResultAP);
2461
2462
return CheckFloatResult(S, OpPC, F, Status, FPO);
2463
}
2464
2465
static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC,
2466
uint32_t BitWidth, uint32_t FPOI) {
2467
const Floating &F = S.Stk.pop<Floating>();
2468
2469
APSInt Result(BitWidth, /*IsUnsigned=*/false);
2470
auto Status = F.convertToInteger(Result);
2471
2472
// Float-to-Integral overflow check.
2473
if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite())
2474
return handleOverflow(S, OpPC, F.getAPFloat());
2475
2476
FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
2477
2478
auto ResultAP = S.allocAP<IntegralAP<true>>(BitWidth);
2479
ResultAP.copy(Result);
2480
2481
S.Stk.push<IntegralAP<true>>(ResultAP);
2482
2483
return CheckFloatResult(S, OpPC, F, Status, FPO);
2484
}
2485
2486
bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC,
2487
const Pointer &Ptr, unsigned BitWidth);
2488
bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth);
2489
bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth);
2490
2491
template <PrimType Name, class T = typename PrimConv<Name>::T>
2492
bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
2493
const Pointer &Ptr = S.Stk.pop<Pointer>();
2494
2495
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
2496
<< diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
2497
<< S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
2498
2499
if (!CheckPointerToIntegralCast(S, OpPC, Ptr, T::bitWidth()))
2500
return Invalid(S, OpPC);
2501
2502
S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
2503
return true;
2504
}
2505
2506
template <PrimType Name, class T = typename PrimConv<Name>::T>
2507
static inline bool CastIntegralFixedPoint(InterpState &S, CodePtr OpPC,
2508
uint32_t FPS) {
2509
const T &Int = S.Stk.pop<T>();
2510
2511
FixedPointSemantics Sem = FixedPointSemantics::getFromOpaqueInt(FPS);
2512
2513
bool Overflow;
2514
FixedPoint Result = FixedPoint::from(Int.toAPSInt(), Sem, &Overflow);
2515
2516
if (Overflow && !handleFixedPointOverflow(S, OpPC, Result))
2517
return false;
2518
2519
S.Stk.push<FixedPoint>(Result);
2520
return true;
2521
}
2522
2523
static inline bool CastFloatingFixedPoint(InterpState &S, CodePtr OpPC,
2524
uint32_t FPS) {
2525
const auto &Float = S.Stk.pop<Floating>();
2526
2527
FixedPointSemantics Sem = FixedPointSemantics::getFromOpaqueInt(FPS);
2528
2529
bool Overflow;
2530
FixedPoint Result = FixedPoint::from(Float.getAPFloat(), Sem, &Overflow);
2531
2532
if (Overflow && !handleFixedPointOverflow(S, OpPC, Result))
2533
return false;
2534
2535
S.Stk.push<FixedPoint>(Result);
2536
return true;
2537
}
2538
2539
static inline bool CastFixedPointFloating(InterpState &S, CodePtr OpPC,
2540
const llvm::fltSemantics *Sem) {
2541
const auto &Fixed = S.Stk.pop<FixedPoint>();
2542
Floating Result = S.allocFloat(*Sem);
2543
Result.copy(Fixed.toFloat(Sem));
2544
S.Stk.push<Floating>(Result);
2545
return true;
2546
}
2547
2548
template <PrimType Name, class T = typename PrimConv<Name>::T>
2549
static inline bool CastFixedPointIntegral(InterpState &S, CodePtr OpPC) {
2550
const auto &Fixed = S.Stk.pop<FixedPoint>();
2551
2552
bool Overflow;
2553
APSInt Int = Fixed.toInt(T::bitWidth(), T::isSigned(), &Overflow);
2554
2555
if (Overflow && !handleOverflow(S, OpPC, Int))
2556
return false;
2557
2558
S.Stk.push<T>(Int);
2559
return true;
2560
}
2561
2562
static inline bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr) {
2563
const auto &Ptr = S.Stk.peek<Pointer>();
2564
2565
if (SrcIsVoidPtr && S.getLangOpts().CPlusPlus) {
2566
bool HasValidResult = !Ptr.isZero();
2567
2568
if (HasValidResult) {
2569
if (S.getStdAllocatorCaller("allocate"))
2570
return true;
2571
2572
const auto &E = cast<CastExpr>(S.Current->getExpr(OpPC));
2573
if (S.getLangOpts().CPlusPlus26 &&
2574
S.getASTContext().hasSimilarType(Ptr.getType(),
2575
E->getType()->getPointeeType()))
2576
return true;
2577
2578
S.CCEDiag(E, diag::note_constexpr_invalid_void_star_cast)
2579
<< E->getSubExpr()->getType() << S.getLangOpts().CPlusPlus26
2580
<< Ptr.getType().getCanonicalType() << E->getType()->getPointeeType();
2581
} else if (!S.getLangOpts().CPlusPlus26) {
2582
const SourceInfo &E = S.Current->getSource(OpPC);
2583
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
2584
<< diag::ConstexprInvalidCastKind::CastFrom << "'void *'"
2585
<< S.Current->getRange(OpPC);
2586
}
2587
} else {
2588
const SourceInfo &E = S.Current->getSource(OpPC);
2589
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
2590
<< diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
2591
<< S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
2592
}
2593
2594
return true;
2595
}
2596
2597
//===----------------------------------------------------------------------===//
2598
// Zero, Nullptr
2599
//===----------------------------------------------------------------------===//
2600
2601
template <PrimType Name, class T = typename PrimConv<Name>::T>
2602
bool Zero(InterpState &S, CodePtr OpPC) {
2603
S.Stk.push<T>(T::zero());
2604
return true;
2605
}
2606
2607
static inline bool ZeroIntAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
2608
auto Result = S.allocAP<IntegralAP<false>>(BitWidth);
2609
if (!Result.singleWord())
2610
std::memset(Result.Memory, 0, Result.numWords() * sizeof(uint64_t));
2611
S.Stk.push<IntegralAP<false>>(Result);
2612
return true;
2613
}
2614
2615
static inline bool ZeroIntAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
2616
auto Result = S.allocAP<IntegralAP<true>>(BitWidth);
2617
if (!Result.singleWord())
2618
std::memset(Result.Memory, 0, Result.numWords() * sizeof(uint64_t));
2619
S.Stk.push<IntegralAP<true>>(Result);
2620
return true;
2621
}
2622
2623
template <PrimType Name, class T = typename PrimConv<Name>::T>
2624
inline bool Null(InterpState &S, CodePtr OpPC, uint64_t Value,
2625
const Descriptor *Desc) {
2626
// FIXME(perf): This is a somewhat often-used function and the value of a
2627
// null pointer is almost always 0.
2628
S.Stk.push<T>(Value, Desc);
2629
return true;
2630
}
2631
2632
template <PrimType Name, class T = typename PrimConv<Name>::T>
2633
inline bool IsNonNull(InterpState &S, CodePtr OpPC) {
2634
const auto &P = S.Stk.pop<T>();
2635
if (P.isWeak())
2636
return false;
2637
S.Stk.push<Boolean>(Boolean::from(!P.isZero()));
2638
return true;
2639
}
2640
2641
//===----------------------------------------------------------------------===//
2642
// This, ImplicitThis
2643
//===----------------------------------------------------------------------===//
2644
2645
inline bool This(InterpState &S, CodePtr OpPC) {
2646
// Cannot read 'this' in this mode.
2647
if (S.checkingPotentialConstantExpression()) {
2648
return false;
2649
}
2650
2651
const Pointer &This = S.Current->getThis();
2652
if (!CheckThis(S, OpPC, This))
2653
return false;
2654
2655
// Ensure the This pointer has been cast to the correct base.
2656
if (!This.isDummy()) {
2657
assert(isa<CXXMethodDecl>(S.Current->getFunction()->getDecl()));
2658
if (!This.isTypeidPointer()) {
2659
[[maybe_unused]] const Record *R = This.getRecord();
2660
if (!R)
2661
R = This.narrow().getRecord();
2662
assert(R);
2663
assert(R->getDecl() ==
2664
cast<CXXMethodDecl>(S.Current->getFunction()->getDecl())
2665
->getParent());
2666
}
2667
}
2668
2669
S.Stk.push<Pointer>(This);
2670
return true;
2671
}
2672
2673
inline bool RVOPtr(InterpState &S, CodePtr OpPC) {
2674
assert(S.Current->getFunction()->hasRVO());
2675
if (S.checkingPotentialConstantExpression())
2676
return false;
2677
S.Stk.push<Pointer>(S.Current->getRVOPtr());
2678
return true;
2679
}
2680
2681
//===----------------------------------------------------------------------===//
2682
// Shr, Shl
2683
//===----------------------------------------------------------------------===//
2684
2685
template <class LT, class RT, ShiftDir Dir>
2686
inline bool DoShift(InterpState &S, CodePtr OpPC, LT &LHS, RT &RHS,
2687
LT *Result) {
2688
static_assert(!needsAlloc<LT>());
2689
const unsigned Bits = LHS.bitWidth();
2690
2691
// OpenCL 6.3j: shift values are effectively % word size of LHS.
2692
if (S.getLangOpts().OpenCL)
2693
RT::bitAnd(RHS, RT::from(LHS.bitWidth() - 1, RHS.bitWidth()),
2694
RHS.bitWidth(), &RHS);
2695
2696
if (RHS.isNegative()) {
2697
// During constant-folding, a negative shift is an opposite shift. Such a
2698
// shift is not a constant expression.
2699
const SourceInfo &Loc = S.Current->getSource(OpPC);
2700
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
2701
if (!S.noteUndefinedBehavior())
2702
return false;
2703
RHS = -RHS;
2704
return DoShift<LT, RT,
2705
Dir == ShiftDir::Left ? ShiftDir::Right : ShiftDir::Left>(
2706
S, OpPC, LHS, RHS, Result);
2707
}
2708
2709
if (!CheckShift<Dir>(S, OpPC, LHS, RHS, Bits))
2710
return false;
2711
2712
// Limit the shift amount to Bits - 1. If this happened,
2713
// it has already been diagnosed by CheckShift() above,
2714
// but we still need to handle it.
2715
// Note that we have to be extra careful here since we're doing the shift in
2716
// any case, but we need to adjust the shift amount or the way we do the shift
2717
// for the potential error cases.
2718
typename LT::AsUnsigned R;
2719
unsigned MaxShiftAmount = LHS.bitWidth() - 1;
2720
if constexpr (Dir == ShiftDir::Left) {
2721
if (Compare(RHS, RT::from(MaxShiftAmount, RHS.bitWidth())) ==
2722
ComparisonCategoryResult::Greater) {
2723
if (LHS.isNegative())
2724
R = LT::AsUnsigned::zero(LHS.bitWidth());
2725
else {
2726
RHS = RT::from(LHS.countLeadingZeros(), RHS.bitWidth());
2727
LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
2728
LT::AsUnsigned::from(RHS, Bits), Bits, &R);
2729
}
2730
} else if (LHS.isNegative()) {
2731
if (LHS.isMin()) {
2732
R = LT::AsUnsigned::zero(LHS.bitWidth());
2733
} else {
2734
// If the LHS is negative, perform the cast and invert the result.
2735
typename LT::AsUnsigned LHSU = LT::AsUnsigned::from(-LHS);
2736
LT::AsUnsigned::shiftLeft(LHSU, LT::AsUnsigned::from(RHS, Bits), Bits,
2737
&R);
2738
R = -R;
2739
}
2740
} else {
2741
// The good case, a simple left shift.
2742
LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
2743
LT::AsUnsigned::from(RHS, Bits), Bits, &R);
2744
}
2745
S.Stk.push<LT>(LT::from(R));
2746
return true;
2747
}
2748
2749
// Right shift.
2750
if (Compare(RHS, RT::from(MaxShiftAmount, RHS.bitWidth())) ==
2751
ComparisonCategoryResult::Greater) {
2752
R = LT::AsUnsigned::from(-1);
2753
} else {
2754
// Do the shift on potentially signed LT, then convert to unsigned type.
2755
LT A;
2756
LT::shiftRight(LHS, LT::from(RHS, Bits), Bits, &A);
2757
R = LT::AsUnsigned::from(A);
2758
}
2759
2760
S.Stk.push<LT>(LT::from(R));
2761
return true;
2762
}
2763
2764
/// A version of DoShift that works on IntegralAP.
2765
template <class LT, class RT, ShiftDir Dir>
2766
inline bool DoShiftAP(InterpState &S, CodePtr OpPC, const APSInt &LHS,
2767
APSInt RHS, LT *Result) {
2768
const unsigned Bits = LHS.getBitWidth();
2769
2770
// OpenCL 6.3j: shift values are effectively % word size of LHS.
2771
if (S.getLangOpts().OpenCL)
2772
RHS &=
2773
APSInt(llvm::APInt(RHS.getBitWidth(), static_cast<uint64_t>(Bits - 1)),
2774
RHS.isUnsigned());
2775
2776
if (RHS.isNegative()) {
2777
// During constant-folding, a negative shift is an opposite shift. Such a
2778
// shift is not a constant expression.
2779
const SourceInfo &Loc = S.Current->getSource(OpPC);
2780
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS; //.toAPSInt();
2781
if (!S.noteUndefinedBehavior())
2782
return false;
2783
return DoShiftAP<LT, RT,
2784
Dir == ShiftDir::Left ? ShiftDir::Right : ShiftDir::Left>(
2785
S, OpPC, LHS, -RHS, Result);
2786
}
2787
2788
if (!CheckShift<Dir>(S, OpPC, static_cast<LT>(LHS), static_cast<RT>(RHS),
2789
Bits))
2790
return false;
2791
2792
unsigned SA = (unsigned)RHS.getLimitedValue(Bits - 1);
2793
if constexpr (Dir == ShiftDir::Left) {
2794
if constexpr (needsAlloc<LT>())
2795
Result->copy(LHS << SA);
2796
else
2797
*Result = LT(LHS << SA);
2798
} else {
2799
if constexpr (needsAlloc<LT>())
2800
Result->copy(LHS >> SA);
2801
else
2802
*Result = LT(LHS >> SA);
2803
}
2804
2805
S.Stk.push<LT>(*Result);
2806
return true;
2807
}
2808
2809
template <PrimType NameL, PrimType NameR>
2810
inline bool Shr(InterpState &S, CodePtr OpPC) {
2811
using LT = typename PrimConv<NameL>::T;
2812
using RT = typename PrimConv<NameR>::T;
2813
auto RHS = S.Stk.pop<RT>();
2814
auto LHS = S.Stk.pop<LT>();
2815
2816
if constexpr (needsAlloc<LT>() || needsAlloc<RT>()) {
2817
LT Result;
2818
if constexpr (needsAlloc<LT>())
2819
Result = S.allocAP<LT>(LHS.bitWidth());
2820
return DoShiftAP<LT, RT, ShiftDir::Right>(S, OpPC, LHS.toAPSInt(),
2821
RHS.toAPSInt(), &Result);
2822
} else {
2823
LT Result;
2824
return DoShift<LT, RT, ShiftDir::Right>(S, OpPC, LHS, RHS, &Result);
2825
}
2826
}
2827
2828
template <PrimType NameL, PrimType NameR>
2829
inline bool Shl(InterpState &S, CodePtr OpPC) {
2830
using LT = typename PrimConv<NameL>::T;
2831
using RT = typename PrimConv<NameR>::T;
2832
auto RHS = S.Stk.pop<RT>();
2833
auto LHS = S.Stk.pop<LT>();
2834
2835
if constexpr (needsAlloc<LT>() || needsAlloc<RT>()) {
2836
LT Result;
2837
if constexpr (needsAlloc<LT>())
2838
Result = S.allocAP<LT>(LHS.bitWidth());
2839
return DoShiftAP<LT, RT, ShiftDir::Left>(S, OpPC, LHS.toAPSInt(),
2840
RHS.toAPSInt(), &Result);
2841
} else {
2842
LT Result;
2843
return DoShift<LT, RT, ShiftDir::Left>(S, OpPC, LHS, RHS, &Result);
2844
}
2845
}
2846
2847
static inline bool ShiftFixedPoint(InterpState &S, CodePtr OpPC, bool Left) {
2848
const auto &RHS = S.Stk.pop<FixedPoint>();
2849
const auto &LHS = S.Stk.pop<FixedPoint>();
2850
llvm::FixedPointSemantics LHSSema = LHS.getSemantics();
2851
2852
unsigned ShiftBitWidth =
2853
LHSSema.getWidth() - (unsigned)LHSSema.hasUnsignedPadding() - 1;
2854
2855
// Embedded-C 4.1.6.2.2:
2856
// The right operand must be nonnegative and less than the total number
2857
// of (nonpadding) bits of the fixed-point operand ...
2858
if (RHS.isNegative()) {
2859
S.CCEDiag(S.Current->getLocation(OpPC), diag::note_constexpr_negative_shift)
2860
<< RHS.toAPSInt();
2861
} else if (static_cast<unsigned>(RHS.toAPSInt().getLimitedValue(
2862
ShiftBitWidth)) != RHS.toAPSInt()) {
2863
const Expr *E = S.Current->getExpr(OpPC);
2864
S.CCEDiag(E, diag::note_constexpr_large_shift)
2865
<< RHS.toAPSInt() << E->getType() << ShiftBitWidth;
2866
}
2867
2868
FixedPoint Result;
2869
if (Left) {
2870
if (FixedPoint::shiftLeft(LHS, RHS, ShiftBitWidth, &Result) &&
2871
!handleFixedPointOverflow(S, OpPC, Result))
2872
return false;
2873
} else {
2874
if (FixedPoint::shiftRight(LHS, RHS, ShiftBitWidth, &Result) &&
2875
!handleFixedPointOverflow(S, OpPC, Result))
2876
return false;
2877
}
2878
2879
S.Stk.push<FixedPoint>(Result);
2880
return true;
2881
}
2882
2883
//===----------------------------------------------------------------------===//
2884
// NoRet
2885
//===----------------------------------------------------------------------===//
2886
2887
inline bool NoRet(InterpState &S, CodePtr OpPC) {
2888
SourceLocation EndLoc = S.Current->getCallee()->getEndLoc();
2889
S.FFDiag(EndLoc, diag::note_constexpr_no_return);
2890
return false;
2891
}
2892
2893
//===----------------------------------------------------------------------===//
2894
// NarrowPtr, ExpandPtr
2895
//===----------------------------------------------------------------------===//
2896
2897
inline bool NarrowPtr(InterpState &S, CodePtr OpPC) {
2898
const Pointer &Ptr = S.Stk.pop<Pointer>();
2899
S.Stk.push<Pointer>(Ptr.narrow());
2900
return true;
2901
}
2902
2903
inline bool ExpandPtr(InterpState &S, CodePtr OpPC) {
2904
const Pointer &Ptr = S.Stk.pop<Pointer>();
2905
if (Ptr.isBlockPointer())
2906
S.Stk.push<Pointer>(Ptr.expand());
2907
else
2908
S.Stk.push<Pointer>(Ptr);
2909
return true;
2910
}
2911
2912
// 1) Pops an integral value from the stack
2913
// 2) Peeks a pointer
2914
// 3) Pushes a new pointer that's a narrowed array
2915
// element of the peeked pointer with the value
2916
// from 1) added as offset.
2917
//
2918
// This leaves the original pointer on the stack and pushes a new one
2919
// with the offset applied and narrowed.
2920
template <PrimType Name, class T = typename PrimConv<Name>::T>
2921
inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
2922
const T &Offset = S.Stk.pop<T>();
2923
const Pointer &Ptr = S.Stk.peek<Pointer>();
2924
2925
if (!Ptr.isZero() && !Offset.isZero()) {
2926
if (!CheckArray(S, OpPC, Ptr))
2927
return false;
2928
}
2929
2930
if (Offset.isZero()) {
2931
if (Ptr.getFieldDesc()->isArray() && Ptr.getIndex() == 0) {
2932
S.Stk.push<Pointer>(Ptr.atIndex(0).narrow());
2933
return true;
2934
}
2935
S.Stk.push<Pointer>(Ptr);
2936
return true;
2937
}
2938
2939
assert(!Offset.isZero());
2940
2941
if (std::optional<Pointer> Result =
2942
OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr)) {
2943
S.Stk.push<Pointer>(Result->narrow());
2944
return true;
2945
}
2946
2947
return false;
2948
}
2949
2950
template <PrimType Name, class T = typename PrimConv<Name>::T>
2951
inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
2952
const T &Offset = S.Stk.pop<T>();
2953
const Pointer &Ptr = S.Stk.pop<Pointer>();
2954
2955
if (!Ptr.isZero() && !Offset.isZero()) {
2956
if (!CheckArray(S, OpPC, Ptr))
2957
return false;
2958
}
2959
2960
if (Offset.isZero()) {
2961
if (Ptr.getFieldDesc()->isArray() && Ptr.getIndex() == 0) {
2962
S.Stk.push<Pointer>(Ptr.atIndex(0).narrow());
2963
return true;
2964
}
2965
S.Stk.push<Pointer>(Ptr);
2966
return true;
2967
}
2968
2969
assert(!Offset.isZero());
2970
2971
if (std::optional<Pointer> Result =
2972
OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr)) {
2973
S.Stk.push<Pointer>(Result->narrow());
2974
return true;
2975
}
2976
return false;
2977
}
2978
2979
template <PrimType Name, class T = typename PrimConv<Name>::T>
2980
inline bool ArrayElem(InterpState &S, CodePtr OpPC, uint32_t Index) {
2981
const Pointer &Ptr = S.Stk.peek<Pointer>();
2982
2983
if (!CheckLoad(S, OpPC, Ptr))
2984
return false;
2985
2986
assert(Ptr.atIndex(Index).getFieldDesc()->getPrimType() == Name);
2987
S.Stk.push<T>(Ptr.atIndex(Index).deref<T>());
2988
return true;
2989
}
2990
2991
template <PrimType Name, class T = typename PrimConv<Name>::T>
2992
inline bool ArrayElemPop(InterpState &S, CodePtr OpPC, uint32_t Index) {
2993
const Pointer &Ptr = S.Stk.pop<Pointer>();
2994
2995
if (!CheckLoad(S, OpPC, Ptr))
2996
return false;
2997
2998
assert(Ptr.atIndex(Index).getFieldDesc()->getPrimType() == Name);
2999
S.Stk.push<T>(Ptr.atIndex(Index).deref<T>());
3000
return true;
3001
}
3002
3003
template <PrimType Name, class T = typename PrimConv<Name>::T>
3004
inline bool CopyArray(InterpState &S, CodePtr OpPC, uint32_t SrcIndex,
3005
uint32_t DestIndex, uint32_t Size) {
3006
const auto &SrcPtr = S.Stk.pop<Pointer>();
3007
const auto &DestPtr = S.Stk.peek<Pointer>();
3008
3009
for (uint32_t I = 0; I != Size; ++I) {
3010
const Pointer &SP = SrcPtr.atIndex(SrcIndex + I);
3011
3012
if (!CheckLoad(S, OpPC, SP))
3013
return false;
3014
3015
const Pointer &DP = DestPtr.atIndex(DestIndex + I);
3016
DP.deref<T>() = SP.deref<T>();
3017
DP.initialize();
3018
}
3019
return true;
3020
}
3021
3022
/// Just takes a pointer and checks if it's an incomplete
3023
/// array type.
3024
inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
3025
const Pointer &Ptr = S.Stk.pop<Pointer>();
3026
3027
if (Ptr.isZero()) {
3028
S.Stk.push<Pointer>(Ptr);
3029
return true;
3030
}
3031
3032
if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer))
3033
return false;
3034
3035
if (Ptr.isRoot() || !Ptr.isUnknownSizeArray()) {
3036
S.Stk.push<Pointer>(Ptr.atIndex(0));
3037
return true;
3038
}
3039
3040
const SourceInfo &E = S.Current->getSource(OpPC);
3041
S.FFDiag(E, diag::note_constexpr_unsupported_unsized_array);
3042
3043
return false;
3044
}
3045
3046
inline bool GetFnPtr(InterpState &S, CodePtr OpPC, const Function *Func) {
3047
assert(Func);
3048
S.Stk.push<Pointer>(Func);
3049
return true;
3050
}
3051
3052
template <PrimType Name, class T = typename PrimConv<Name>::T>
3053
inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
3054
const T &IntVal = S.Stk.pop<T>();
3055
3056
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
3057
<< diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
3058
<< S.getLangOpts().CPlusPlus;
3059
3060
S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc);
3061
return true;
3062
}
3063
3064
inline bool GetMemberPtr(InterpState &S, CodePtr OpPC, const ValueDecl *D) {
3065
S.Stk.push<MemberPointer>(D);
3066
return true;
3067
}
3068
3069
inline bool GetMemberPtrBase(InterpState &S, CodePtr OpPC) {
3070
const auto &MP = S.Stk.pop<MemberPointer>();
3071
3072
S.Stk.push<Pointer>(MP.getBase());
3073
return true;
3074
}
3075
3076
inline bool GetMemberPtrDecl(InterpState &S, CodePtr OpPC) {
3077
const auto &MP = S.Stk.pop<MemberPointer>();
3078
3079
const auto *FD = cast<FunctionDecl>(MP.getDecl());
3080
const auto *Func = S.getContext().getOrCreateFunction(FD);
3081
3082
S.Stk.push<Pointer>(Func);
3083
return true;
3084
}
3085
3086
/// Just emit a diagnostic. The expression that caused emission of this
3087
/// op is not valid in a constant context.
3088
inline bool Invalid(InterpState &S, CodePtr OpPC) {
3089
const SourceLocation &Loc = S.Current->getLocation(OpPC);
3090
S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr)
3091
<< S.Current->getRange(OpPC);
3092
return false;
3093
}
3094
3095
inline bool Unsupported(InterpState &S, CodePtr OpPC) {
3096
const SourceLocation &Loc = S.Current->getLocation(OpPC);
3097
S.FFDiag(Loc, diag::note_constexpr_stmt_expr_unsupported)
3098
<< S.Current->getRange(OpPC);
3099
return false;
3100
}
3101
3102
inline bool StartSpeculation(InterpState &S, CodePtr OpPC) {
3103
++S.SpeculationDepth;
3104
if (S.SpeculationDepth != 1)
3105
return true;
3106
3107
assert(S.PrevDiags == nullptr);
3108
S.PrevDiags = S.getEvalStatus().Diag;
3109
S.getEvalStatus().Diag = nullptr;
3110
return true;
3111
}
3112
inline bool EndSpeculation(InterpState &S, CodePtr OpPC) {
3113
assert(S.SpeculationDepth != 0);
3114
--S.SpeculationDepth;
3115
if (S.SpeculationDepth == 0) {
3116
S.getEvalStatus().Diag = S.PrevDiags;
3117
S.PrevDiags = nullptr;
3118
}
3119
return true;
3120
}
3121
3122
inline bool PushCC(InterpState &S, CodePtr OpPC, bool Value) {
3123
S.ConstantContextOverride = Value;
3124
return true;
3125
}
3126
inline bool PopCC(InterpState &S, CodePtr OpPC) {
3127
S.ConstantContextOverride = std::nullopt;
3128
return true;
3129
}
3130
3131
/// Do nothing and just abort execution.
3132
inline bool Error(InterpState &S, CodePtr OpPC) { return false; }
3133
3134
inline bool SideEffect(InterpState &S, CodePtr OpPC) {
3135
return S.noteSideEffect();
3136
}
3137
3138
/// Same here, but only for casts.
3139
inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind,
3140
bool Fatal) {
3141
const SourceLocation &Loc = S.Current->getLocation(OpPC);
3142
3143
if (Kind == CastKind::Reinterpret) {
3144
S.CCEDiag(Loc, diag::note_constexpr_invalid_cast)
3145
<< static_cast<unsigned>(Kind) << S.Current->getRange(OpPC);
3146
return !Fatal;
3147
} else if (Kind == CastKind::Volatile) {
3148
if (!S.checkingPotentialConstantExpression()) {
3149
const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
3150
if (S.getLangOpts().CPlusPlus)
3151
S.FFDiag(E, diag::note_constexpr_access_volatile_type)
3152
<< AK_Read << E->getSubExpr()->getType();
3153
else
3154
S.FFDiag(E);
3155
}
3156
3157
return false;
3158
} else if (Kind == CastKind::Dynamic) {
3159
assert(!S.getLangOpts().CPlusPlus20);
3160
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
3161
<< diag::ConstexprInvalidCastKind::Dynamic;
3162
return true;
3163
}
3164
3165
return false;
3166
}
3167
3168
inline bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR,
3169
bool InitializerFailed) {
3170
assert(DR);
3171
3172
if (InitializerFailed) {
3173
const SourceInfo &Loc = S.Current->getSource(OpPC);
3174
const auto *VD = cast<VarDecl>(DR->getDecl());
3175
S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
3176
S.Note(VD->getLocation(), diag::note_declared_at);
3177
return false;
3178
}
3179
3180
return CheckDeclRef(S, OpPC, DR);
3181
}
3182
3183
inline bool SizelessVectorElementSize(InterpState &S, CodePtr OpPC) {
3184
if (S.inConstantContext()) {
3185
const SourceRange &ArgRange = S.Current->getRange(OpPC);
3186
const Expr *E = S.Current->getExpr(OpPC);
3187
S.CCEDiag(E, diag::note_constexpr_non_const_vectorelements) << ArgRange;
3188
}
3189
return false;
3190
}
3191
3192
inline bool CheckPseudoDtor(InterpState &S, CodePtr OpPC) {
3193
if (!S.getLangOpts().CPlusPlus20)
3194
S.CCEDiag(S.Current->getSource(OpPC),
3195
diag::note_constexpr_pseudo_destructor);
3196
return true;
3197
}
3198
3199
inline bool Assume(InterpState &S, CodePtr OpPC) {
3200
const auto Val = S.Stk.pop<Boolean>();
3201
3202
if (Val)
3203
return true;
3204
3205
// Else, diagnose.
3206
const SourceLocation &Loc = S.Current->getLocation(OpPC);
3207
S.CCEDiag(Loc, diag::note_constexpr_assumption_failed);
3208
return false;
3209
}
3210
3211
template <PrimType Name, class T = typename PrimConv<Name>::T>
3212
inline bool OffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E) {
3213
llvm::SmallVector<int64_t> ArrayIndices;
3214
for (size_t I = 0; I != E->getNumExpressions(); ++I)
3215
ArrayIndices.emplace_back(S.Stk.pop<int64_t>());
3216
3217
int64_t Result;
3218
if (!InterpretOffsetOf(S, OpPC, E, ArrayIndices, Result))
3219
return false;
3220
3221
S.Stk.push<T>(T::from(Result));
3222
3223
return true;
3224
}
3225
3226
template <PrimType Name, class T = typename PrimConv<Name>::T>
3227
inline bool CheckNonNullArg(InterpState &S, CodePtr OpPC) {
3228
const T &Arg = S.Stk.peek<T>();
3229
if (!Arg.isZero())
3230
return true;
3231
3232
const SourceLocation &Loc = S.Current->getLocation(OpPC);
3233
S.CCEDiag(Loc, diag::note_non_null_attribute_failed);
3234
3235
return false;
3236
}
3237
3238
void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
3239
const APSInt &Value);
3240
3241
template <PrimType Name, class T = typename PrimConv<Name>::T>
3242
inline bool CheckEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED) {
3243
assert(ED);
3244
assert(!ED->isFixed());
3245
3246
if (S.inConstantContext()) {
3247
const APSInt Val = S.Stk.peek<T>().toAPSInt();
3248
diagnoseEnumValue(S, OpPC, ED, Val);
3249
}
3250
return true;
3251
}
3252
3253
/// OldPtr -> Integer -> NewPtr.
3254
template <PrimType TIn, PrimType TOut>
3255
inline bool DecayPtr(InterpState &S, CodePtr OpPC) {
3256
static_assert(isPtrType(TIn) && isPtrType(TOut));
3257
using FromT = typename PrimConv<TIn>::T;
3258
using ToT = typename PrimConv<TOut>::T;
3259
3260
const FromT &OldPtr = S.Stk.pop<FromT>();
3261
3262
if constexpr (std::is_same_v<FromT, FunctionPointer> &&
3263
std::is_same_v<ToT, Pointer>) {
3264
S.Stk.push<Pointer>(OldPtr.getFunction(), OldPtr.getOffset());
3265
return true;
3266
} else if constexpr (std::is_same_v<FromT, Pointer> &&
3267
std::is_same_v<ToT, FunctionPointer>) {
3268
if (OldPtr.isFunctionPointer()) {
3269
S.Stk.push<FunctionPointer>(OldPtr.asFunctionPointer().getFunction(),
3270
OldPtr.getByteOffset());
3271
return true;
3272
}
3273
}
3274
3275
S.Stk.push<ToT>(ToT(OldPtr.getIntegerRepresentation(), nullptr));
3276
return true;
3277
}
3278
3279
inline bool CheckDecl(InterpState &S, CodePtr OpPC, const VarDecl *VD) {
3280
// An expression E is a core constant expression unless the evaluation of E
3281
// would evaluate one of the following: [C++23] - a control flow that passes
3282
// through a declaration of a variable with static or thread storage duration
3283
// unless that variable is usable in constant expressions.
3284
assert(VD->isLocalVarDecl() &&
3285
VD->isStaticLocal()); // Checked before emitting this.
3286
3287
if (VD == S.EvaluatingDecl)
3288
return true;
3289
3290
if (!VD->isUsableInConstantExpressions(S.getASTContext())) {
3291
S.CCEDiag(VD->getLocation(), diag::note_constexpr_static_local)
3292
<< (VD->getTSCSpec() == TSCS_unspecified ? 0 : 1) << VD;
3293
return false;
3294
}
3295
return true;
3296
}
3297
3298
inline bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
3299
assert(Desc);
3300
3301
if (!CheckDynamicMemoryAllocation(S, OpPC))
3302
return false;
3303
3304
DynamicAllocator &Allocator = S.getAllocator();
3305
Block *B = Allocator.allocate(Desc, S.Ctx.getEvalID(),
3306
DynamicAllocator::Form::NonArray);
3307
assert(B);
3308
S.Stk.push<Pointer>(B);
3309
return true;
3310
}
3311
3312
template <PrimType Name, class SizeT = typename PrimConv<Name>::T>
3313
inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
3314
bool IsNoThrow) {
3315
if (!CheckDynamicMemoryAllocation(S, OpPC))
3316
return false;
3317
3318
SizeT NumElements = S.Stk.pop<SizeT>();
3319
if (!CheckArraySize(S, OpPC, &NumElements, primSize(T), IsNoThrow)) {
3320
if (!IsNoThrow)
3321
return false;
3322
3323
// If this failed and is nothrow, just return a null ptr.
3324
S.Stk.push<Pointer>(0, nullptr);
3325
return true;
3326
}
3327
assert(NumElements.isPositive());
3328
3329
if (!CheckArraySize(S, OpPC, static_cast<uint64_t>(NumElements)))
3330
return false;
3331
3332
DynamicAllocator &Allocator = S.getAllocator();
3333
Block *B =
3334
Allocator.allocate(Source, T, static_cast<size_t>(NumElements),
3335
S.Ctx.getEvalID(), DynamicAllocator::Form::Array);
3336
assert(B);
3337
if (NumElements.isZero())
3338
S.Stk.push<Pointer>(B);
3339
else
3340
S.Stk.push<Pointer>(Pointer(B).atIndex(0));
3341
return true;
3342
}
3343
3344
template <PrimType Name, class SizeT = typename PrimConv<Name>::T>
3345
inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc,
3346
bool IsNoThrow) {
3347
if (!CheckDynamicMemoryAllocation(S, OpPC))
3348
return false;
3349
3350
SizeT NumElements = S.Stk.pop<SizeT>();
3351
if (!CheckArraySize(S, OpPC, &NumElements, ElementDesc->getSize(),
3352
IsNoThrow)) {
3353
if (!IsNoThrow)
3354
return false;
3355
3356
// If this failed and is nothrow, just return a null ptr.
3357
S.Stk.push<Pointer>(0, ElementDesc);
3358
return true;
3359
}
3360
assert(NumElements.isPositive());
3361
3362
if (!CheckArraySize(S, OpPC, static_cast<uint64_t>(NumElements)))
3363
return false;
3364
3365
DynamicAllocator &Allocator = S.getAllocator();
3366
Block *B =
3367
Allocator.allocate(ElementDesc, static_cast<size_t>(NumElements),
3368
S.Ctx.getEvalID(), DynamicAllocator::Form::Array);
3369
assert(B);
3370
if (NumElements.isZero())
3371
S.Stk.push<Pointer>(B);
3372
else
3373
S.Stk.push<Pointer>(Pointer(B).atIndex(0));
3374
3375
return true;
3376
}
3377
3378
bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
3379
bool IsGlobalDelete);
3380
3381
static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) {
3382
S.Stk.push<Boolean>(Boolean::from(S.inConstantContext()));
3383
return true;
3384
}
3385
3386
static inline bool CheckAllocations(InterpState &S, CodePtr OpPC) {
3387
return S.maybeDiagnoseDanglingAllocations();
3388
}
3389
3390
/// Check if the initializer and storage types of a placement-new expression
3391
/// match.
3392
bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
3393
std::optional<uint64_t> ArraySize = std::nullopt);
3394
3395
template <PrimType Name, class T = typename PrimConv<Name>::T>
3396
bool CheckNewTypeMismatchArray(InterpState &S, CodePtr OpPC, const Expr *E) {
3397
const auto &Size = S.Stk.pop<T>();
3398
return CheckNewTypeMismatch(S, OpPC, E, static_cast<uint64_t>(Size));
3399
}
3400
bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E);
3401
3402
template <PrimType Name, class T = typename PrimConv<Name>::T>
3403
inline bool BitCastPrim(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
3404
uint32_t ResultBitWidth,
3405
const llvm::fltSemantics *Sem) {
3406
const Pointer &FromPtr = S.Stk.pop<Pointer>();
3407
3408
if (!CheckLoad(S, OpPC, FromPtr))
3409
return false;
3410
3411
if constexpr (std::is_same_v<T, Pointer>) {
3412
// The only pointer type we can validly bitcast to is nullptr_t.
3413
S.Stk.push<Pointer>();
3414
return true;
3415
} else {
3416
3417
size_t BuffSize = ResultBitWidth / 8;
3418
llvm::SmallVector<std::byte> Buff(BuffSize);
3419
bool HasIndeterminateBits = false;
3420
3421
Bits FullBitWidth(ResultBitWidth);
3422
Bits BitWidth = FullBitWidth;
3423
3424
if constexpr (std::is_same_v<T, Floating>) {
3425
assert(Sem);
3426
BitWidth = Bits(llvm::APFloatBase::getSizeInBits(*Sem));
3427
}
3428
3429
if (!DoBitCast(S, OpPC, FromPtr, Buff.data(), BitWidth, FullBitWidth,
3430
HasIndeterminateBits))
3431
return false;
3432
3433
if (!CheckBitCast(S, OpPC, HasIndeterminateBits, TargetIsUCharOrByte))
3434
return false;
3435
3436
if constexpr (std::is_same_v<T, Floating>) {
3437
assert(Sem);
3438
Floating Result = S.allocFloat(*Sem);
3439
Floating::bitcastFromMemory(Buff.data(), *Sem, &Result);
3440
S.Stk.push<Floating>(Result);
3441
3442
// S.Stk.push<Floating>(T::bitcastFromMemory(Buff.data(), *Sem));
3443
} else if constexpr (needsAlloc<T>()) {
3444
T Result = S.allocAP<T>(ResultBitWidth);
3445
T::bitcastFromMemory(Buff.data(), ResultBitWidth, &Result);
3446
S.Stk.push<T>(Result);
3447
} else {
3448
assert(!Sem);
3449
S.Stk.push<T>(T::bitcastFromMemory(Buff.data(), ResultBitWidth));
3450
}
3451
return true;
3452
}
3453
}
3454
3455
inline bool BitCast(InterpState &S, CodePtr OpPC) {
3456
const Pointer &FromPtr = S.Stk.pop<Pointer>();
3457
Pointer &ToPtr = S.Stk.peek<Pointer>();
3458
3459
if (!CheckLoad(S, OpPC, FromPtr))
3460
return false;
3461
3462
if (!DoBitCastPtr(S, OpPC, FromPtr, ToPtr))
3463
return false;
3464
3465
return true;
3466
}
3467
3468
/// Typeid support.
3469
bool GetTypeid(InterpState &S, CodePtr OpPC, const Type *TypePtr,
3470
const Type *TypeInfoType);
3471
bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType);
3472
bool DiagTypeid(InterpState &S, CodePtr OpPC);
3473
3474
inline bool CheckDestruction(InterpState &S, CodePtr OpPC) {
3475
const auto &Ptr = S.Stk.peek<Pointer>();
3476
return CheckDestructor(S, OpPC, Ptr);
3477
}
3478
3479
inline bool CheckArraySize(InterpState &S, CodePtr OpPC, uint64_t NumElems) {
3480
uint64_t Limit = S.getLangOpts().ConstexprStepLimit;
3481
if (NumElems > Limit) {
3482
S.FFDiag(S.Current->getSource(OpPC),
3483
diag::note_constexpr_new_exceeds_limits)
3484
<< NumElems << Limit;
3485
return false;
3486
}
3487
return true;
3488
}
3489
3490
//===----------------------------------------------------------------------===//
3491
// Read opcode arguments
3492
//===----------------------------------------------------------------------===//
3493
3494
template <typename T> inline T ReadArg(InterpState &S, CodePtr &OpPC) {
3495
if constexpr (std::is_pointer<T>::value) {
3496
uint32_t ID = OpPC.read<uint32_t>();
3497
return reinterpret_cast<T>(S.P.getNativePointer(ID));
3498
} else {
3499
return OpPC.read<T>();
3500
}
3501
}
3502
3503
template <> inline Floating ReadArg<Floating>(InterpState &S, CodePtr &OpPC) {
3504
auto &Semantics =
3505
llvm::APFloatBase::EnumToSemantics(Floating::deserializeSemantics(*OpPC));
3506
3507
auto F = S.allocFloat(Semantics);
3508
Floating::deserialize(*OpPC, &F);
3509
OpPC += align(F.bytesToSerialize());
3510
return F;
3511
}
3512
3513
template <>
3514
inline IntegralAP<false> ReadArg<IntegralAP<false>>(InterpState &S,
3515
CodePtr &OpPC) {
3516
uint32_t BitWidth = IntegralAP<false>::deserializeSize(*OpPC);
3517
auto Result = S.allocAP<IntegralAP<false>>(BitWidth);
3518
assert(Result.bitWidth() == BitWidth);
3519
3520
IntegralAP<false>::deserialize(*OpPC, &Result);
3521
OpPC += align(Result.bytesToSerialize());
3522
return Result;
3523
}
3524
3525
template <>
3526
inline IntegralAP<true> ReadArg<IntegralAP<true>>(InterpState &S,
3527
CodePtr &OpPC) {
3528
uint32_t BitWidth = IntegralAP<true>::deserializeSize(*OpPC);
3529
auto Result = S.allocAP<IntegralAP<true>>(BitWidth);
3530
assert(Result.bitWidth() == BitWidth);
3531
3532
IntegralAP<true>::deserialize(*OpPC, &Result);
3533
OpPC += align(Result.bytesToSerialize());
3534
return Result;
3535
}
3536
3537
template <>
3538
inline FixedPoint ReadArg<FixedPoint>(InterpState &S, CodePtr &OpPC) {
3539
FixedPoint FP = FixedPoint::deserialize(*OpPC);
3540
OpPC += align(FP.bytesToSerialize());
3541
return FP;
3542
}
3543
3544
} // namespace interp
3545
} // namespace clang
3546
3547
#endif
3548
3549