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 "Char.h"
20#include "DynamicAllocator.h"
21#include "FixedPoint.h"
22#include "Floating.h"
23#include "Function.h"
24#include "InterpBuiltinBitCast.h"
25#include "InterpFrame.h"
26#include "InterpHelpers.h"
27#include "InterpStack.h"
28#include "InterpState.h"
29#include "MemberPointer.h"
30#include "PrimType.h"
31#include "Program.h"
32#include "Reflect.h"
33#include "State.h"
34#include "clang/AST/ASTContext.h"
35#include "clang/AST/Expr.h"
36#include "llvm/ADT/APFloat.h"
37#include "llvm/ADT/APSInt.h"
38#include "llvm/Support/Compiler.h"
39#include <type_traits>
40
41// preserve_none causes problems when asan is enabled on both AArch64 and other
42// platforms. Disable it until all the bugs are fixed here.
43//
44// See https://github.com/llvm/llvm-project/issues/177519 for AArch64.
45#if !defined(__aarch64__) && !defined(__i386__) && \
46 !__has_feature(address_sanitizer) && \
47 __has_cpp_attribute(clang::preserve_none)
48#define PRESERVE_NONE [[clang::preserve_none]]
49#else
50#define PRESERVE_NONE
51#endif
52
53namespace clang {
54namespace interp {
55
56using APSInt = llvm::APSInt;
57using FixedPointSemantics = llvm::FixedPointSemantics;
58
59/// Checks if a pointer is null.
60bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
61 CheckSubobjectKind CSK);
62
63/// Checks if Ptr is a one-past-the-end pointer.
64bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
65 CheckSubobjectKind CSK);
66
67/// Checks if the dowcast using the given offset is possible with the given
68/// pointer.
69bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
70 uint32_t Offset);
71
72/// Checks if a pointer points to const storage.
73bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
74
75/// Checks if the Descriptor is of a constexpr or const global variable.
76bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
77 AccessKinds AK = AK_Read);
78
79bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
80
81bool diagnoseUninitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
82 AccessKinds AK);
83
84bool diagnoseArrayIndex(InterpState &S, CodePtr OpPC, const APSInt &Index,
85 std::optional<uint64_t> NumElems = std::nullopt,
86 bool IsArray = true);
87
88/// Checks a direct load of a primitive value from a global or local variable.
89bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B);
90bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B);
91
92/// Checks if a value can be stored in a block.
93bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
94 AccessKinds AK = AK_Assign, bool WillBeActivated = false);
95
96/// Checks if a value can be initialized.
97bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
98
99/// Checks the 'this' pointer.
100bool CheckThis(InterpState &S, CodePtr OpPC);
101
102/// Checks if dynamic memory allocation is available in the current
103/// language mode.
104bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC);
105
106/// Check the source of the pointer passed to delete/delete[] has actually
107/// been heap allocated by us.
108bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
109
110/// Sets the given integral value to the pointer, which is of
111/// a std::{weak,partial,strong}_ordering type.
112bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
113 const Pointer &Ptr, const APSInt &IntValue);
114
115bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
116 uint32_t VarArgSize);
117bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
118 uint32_t VarArgSize);
119bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
120 uint32_t VarArgSize);
121bool CallBI(InterpState &S, CodePtr OpPC, const CallExpr *CE,
122 uint32_t BuiltinID);
123bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
124 const CallExpr *CE);
125bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T);
126bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index);
127bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
128 bool TargetIsUCharOrByte);
129bool CheckBCPResult(InterpState &S, const Pointer &Ptr);
130bool checkDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
131bool CheckFunctionDecl(InterpState &S, CodePtr OpPC, const FunctionDecl *FD);
132bool CheckBitCast(InterpState &S, CodePtr OpPC, const Type *TargetType,
133 bool SrcIsVoidPtr);
134bool handleReference(InterpState &S, CodePtr OpPC, Block *B);
135bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind, bool Fatal);
136
137bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
138 const FixedPoint &FP);
139
140bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I);
141bool isConstexprUnknown(const Pointer &P);
142bool isConstexprUnknown(const Block *B);
143bool DynamicCast(InterpState &S, CodePtr OpPC, const Type *DestType,
144 bool IsReferenceCast);
145bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth,
146 uint32_t FPOI);
147bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth,
148 uint32_t FPOI);
149
150enum class ShiftDir { Left, Right };
151
152enum class ShiftFailure {
153 NegativeCount,
154 TooLarge,
155 NegativeLeftOperand,
156 DiscardsBits,
157};
158
159LLVM_ATTRIBUTE_NOINLINE bool diagnoseShiftFailure(InterpState &S, CodePtr OpPC,
160 ShiftFailure Failure,
161 const APSInt *Value = nullptr,
162 unsigned Bits = 0);
163
164/// Checks if the shift operation is legal.
165template <ShiftDir Dir, typename LT, typename RT>
166bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS,
167 unsigned Bits) {
168 if (RHS.isNegative()) {
169 const APSInt Value = RHS.toAPSInt();
170 if (!diagnoseShiftFailure(S, OpPC, Failure: ShiftFailure::NegativeCount, Value: &Value))
171 return false;
172 }
173
174 // C++11 [expr.shift]p1: Shift width must be less than the bit width of
175 // the shifted type.
176 if (Bits > 1 && RHS >= Bits) {
177 const APSInt Value = RHS.toAPSInt();
178 if (!diagnoseShiftFailure(S, OpPC, Failure: ShiftFailure::TooLarge, Value: &Value, Bits))
179 return false;
180 }
181
182 if constexpr (Dir == ShiftDir::Left) {
183 if (LHS.isSigned() && !S.getLangOpts().CPlusPlus20) {
184 // C++11 [expr.shift]p2: A signed left shift must have a non-negative
185 // operand, and must not overflow the corresponding unsigned type.
186 if (LHS.isNegative()) {
187 const APSInt Value = LHS.toAPSInt();
188 if (!diagnoseShiftFailure(S, OpPC, Failure: ShiftFailure::NegativeLeftOperand,
189 Value: &Value))
190 return false;
191 } else if (LHS.toUnsigned().countLeadingZeros() <
192 static_cast<unsigned>(RHS)) {
193 if (!diagnoseShiftFailure(S, OpPC, Failure: ShiftFailure::DiscardsBits))
194 return false;
195 }
196 }
197 }
198
199 // C++2a [expr.shift]p2: [P0907R4]:
200 // E1 << E2 is the unique value congruent to
201 // E1 x 2^E2 module 2^N.
202 return true;
203}
204
205/// Checks if Div/Rem operation on LHS and RHS is valid.
206template <typename T>
207bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
208
209 if constexpr (isIntegralOrPointer<T>()) {
210 if (!LHS.isNumber() || !RHS.isNumber())
211 return false;
212 }
213
214 if (RHS.isZero()) {
215 const auto *Op = cast<BinaryOperator>(Val: S.Current->getExpr(PC: OpPC));
216 if constexpr (std::is_same_v<T, Floating>) {
217 S.CCEDiag(E: Op, DiagId: diag::note_expr_divide_by_zero)
218 << Op->getRHS()->getSourceRange();
219 return true;
220 }
221
222 S.FFDiag(E: Op, DiagId: diag::note_expr_divide_by_zero)
223 << Op->getRHS()->getSourceRange();
224 return false;
225 }
226
227 if constexpr (!std::is_same_v<T, FixedPoint>) {
228 if (LHS.isSigned() && LHS.isMin() && RHS.isNegative() && RHS.isMinusOne()) {
229 APSInt LHSInt = LHS.toAPSInt();
230 SmallString<32> Trunc;
231 (-LHSInt.extend(width: LHSInt.getBitWidth() + 1)).toString(Str&: Trunc, Radix: 10);
232 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
233 const Expr *E = S.Current->getExpr(PC: OpPC);
234 S.CCEDiag(SI: Loc, DiagId: diag::note_constexpr_overflow) << Trunc << E->getType();
235 return false;
236 }
237 }
238 return true;
239}
240
241/// Checks if the result of a floating-point operation is valid
242/// in the current context.
243/// Notes:
244/// - CheckFloatStatus is the same as
245/// checkFloatingPointResultForConstantFolding in
246/// clang/lib/AST/ExprConstant.cpp.
247/// - CheckFloatResult will also check if the result is NaN, in addition to
248/// CheckFloatStatus's checks.
249// FIXME: P3899R3 (adopted by WG21 in June 2026) likely makes this interface
250// obsolete.
251// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p3899r3.html
252// Also see the comment:
253// https://github.com/llvm/llvm-project/pull/213750/changes/2fea01449764e23b84ce6790bc7121d369546192#r3708712572
254bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
255 APFloat::opStatus Status, FPOptions FPO);
256
257/// Check if the given floating-point evaluation status is allowed for
258/// compile-time constant folding during translation (as opposed to mandatory
259/// constant expression evaluation).
260bool CheckFloatStatus(InterpState &S, CodePtr OpPC, APFloat::opStatus Status,
261 FPOptions FPO);
262
263/// Checks why the given DeclRefExpr is invalid.
264bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR);
265bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR,
266 bool InitializerFailed);
267
268/// DerivedToBaseMemberPointer
269bool CastMemberPtrBasePop(InterpState &S, int32_t Off,
270 const RecordDecl *BaseDecl);
271/// BaseToDerivedMemberPointer
272bool CastMemberPtrDerivedPop(InterpState &S, int32_t Off,
273 const RecordDecl *BaseDecl);
274enum class ArithOp { Add, Sub };
275
276//===----------------------------------------------------------------------===//
277// Returning values
278//===----------------------------------------------------------------------===//
279
280void cleanupAfterFunctionCall(InterpState &S, const Function *Func);
281
282template <PrimType Name, class T = typename PrimConv<Name>::T>
283PRESERVE_NONE bool Ret(InterpState &S) {
284 const T &Ret = S.Stk.pop<T>();
285
286 assert(S.Current);
287#ifndef NDEBUG
288 assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
289#endif
290
291 // This only happens via Context::Run().
292 if (S.Current->isBottomFrame())
293 return true;
294
295 cleanupAfterFunctionCall(S, Func: S.Current->getFunction());
296 S.PC = S.Current->getRetPC();
297 S.Stk.push<T>(Ret);
298 return true;
299}
300
301PRESERVE_NONE inline bool RetVoid(InterpState &S) {
302 assert(S.Current);
303#ifndef NDEBUG
304 assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
305#endif
306
307 // This only happens via Context::Run().
308 if (S.Current->isBottomFrame())
309 return true;
310
311 cleanupAfterFunctionCall(S, Func: S.Current->getFunction());
312 S.PC = S.Current->getRetPC();
313 return true;
314}
315
316//===----------------------------------------------------------------------===//
317// Add, Sub, Mul
318//===----------------------------------------------------------------------===//
319
320template <typename T, bool (*OpFW)(T, T, unsigned, T *),
321 template <typename U> class OpAP>
322bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS,
323 const T &RHS) {
324 // Should've been handled before.
325 if constexpr (isIntegralOrPointer<T>()) {
326 assert(LHS.isNumber() && RHS.isNumber());
327 }
328
329 // Fast path - add the numbers with fixed width.
330 T Result;
331 if constexpr (needsAlloc<T>())
332 Result = S.allocAP<T>(LHS.bitWidth());
333
334 if (!OpFW(LHS, RHS, Bits, &Result)) {
335 S.Stk.push<T>(Result);
336 return true;
337 }
338 // If for some reason evaluation continues, use the truncated results.
339 S.Stk.push<T>(Result);
340
341 // Short-circuit fixed-points here since the error handling is easier.
342 if constexpr (std::is_same_v<T, FixedPoint>)
343 return handleFixedPointOverflow(S, OpPC, Result);
344
345 // If wrapping is enabled, the new value is fine.
346 if (S.Current->getExpr(PC: OpPC)->getType().isWrapType())
347 return true;
348
349 // Slow path - compute the result using another bit of precision.
350 APSInt Value = OpAP<APSInt>()(LHS.toAPSInt(Bits), RHS.toAPSInt(Bits));
351
352 // Report undefined behaviour, stopping if required.
353 if (S.checkingForUndefinedBehavior()) {
354 const Expr *E = S.Current->getExpr(PC: OpPC);
355 QualType Type = E->getType();
356 SmallString<32> Trunc;
357 Value.trunc(width: Result.bitWidth())
358 .toString(Trunc, 10, Result.isSigned(), /*formatAsCLiteral=*/false,
359 /*UpperCase=*/true, /*InsertSeparators=*/true);
360 S.report(Loc: E->getExprLoc(), DiagId: diag::warn_integer_constant_overflow)
361 << Trunc << Type << E->getSourceRange();
362 }
363
364 if (!handleOverflow(S, OpPC, SrcValue: Value)) {
365 S.Stk.pop<T>();
366 return false;
367 }
368 return true;
369}
370
371// Add or subtract an integer-thats-actually-a-pointer and one real integer.
372template <typename T, template <typename U> class Op>
373static bool AddSubNonNumber(InterpState &S, CodePtr OpPC, T LHS, T RHS) {
374 assert(!LHS.isNumber() || !RHS.isNumber());
375
376 typename T::ReprT Number;
377 const void *Ptr;
378 typename T::ReprT Offset;
379 IntegralKind Kind;
380 if (LHS.isNumber()) {
381 if (RHS.getKind() == IntegralKind::AddrLabelDiff)
382 return Invalid(S, OpPC);
383
384 Number = static_cast<typename T::ReprT>(LHS);
385 Ptr = RHS.getPtr();
386 Offset = RHS.getOffset();
387 Kind = RHS.getKind();
388 } else {
389 assert(RHS.isNumber());
390 if (LHS.getKind() == IntegralKind::AddrLabelDiff)
391 return Invalid(S, OpPC);
392
393 Number = static_cast<typename T::ReprT>(RHS);
394 Ptr = LHS.getPtr();
395 Offset = LHS.getOffset();
396 Kind = LHS.getKind();
397 }
398
399 S.Stk.push<T>(Kind, Ptr, Op<int32_t>()(Offset, Number));
400 return true;
401}
402
403template <PrimType Name, class T = typename PrimConv<Name>::T>
404bool Add(InterpState &S, CodePtr OpPC) {
405 const T &RHS = S.Stk.pop<T>();
406 const T &LHS = S.Stk.pop<T>();
407 const unsigned Bits = RHS.bitWidth() + 1;
408
409 if constexpr (isIntegralOrPointer<T>()) {
410 if (LHS.isNumber() != RHS.isNumber())
411 return AddSubNonNumber<T, std::plus>(S, OpPC, LHS, RHS);
412 else if (LHS.isNumber() && RHS.isNumber())
413 ; // Fall through to proper addition below.
414 else
415 return false; // Reject everything else.
416 }
417
418 return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS);
419}
420
421inline bool Addf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
422 const Floating &RHS = S.Stk.pop<Floating>();
423 const Floating &LHS = S.Stk.pop<Floating>();
424
425 FPOptions FPO = FPOptions::getFromOpaqueInt(Value: FPOI);
426 Floating Result = S.allocFloat(Sem: LHS.getSemantics());
427 auto Status = Floating::add(A: LHS, B: RHS, RM: getRoundingMode(FPO), R: &Result);
428 S.Stk.push<Floating>(Args&: Result);
429 return CheckFloatResult(S, OpPC, Result, Status, FPO);
430}
431
432template <PrimType Name, class T = typename PrimConv<Name>::T>
433bool Sub(InterpState &S, CodePtr OpPC) {
434 const T &RHS = S.Stk.pop<T>();
435 const T &LHS = S.Stk.pop<T>();
436 const unsigned Bits = RHS.bitWidth() + 1;
437
438 if constexpr (isIntegralOrPointer<T>()) {
439 // Handle (int)&&a - (int)&&b.
440 // Both operands should be integrals that point to labels and the result is
441 // a AddrLabelDiff integral.
442 if (LHS.getKind() == IntegralKind::LabelAddress ||
443 RHS.getKind() == IntegralKind::LabelAddress) {
444 const auto *A = LHS.getKind() == IntegralKind::LabelAddress
445 ? reinterpret_cast<const Expr *>(LHS.getPtr())
446 : nullptr;
447 const auto *B = RHS.getKind() == IntegralKind::LabelAddress
448 ? reinterpret_cast<const Expr *>(RHS.getPtr())
449 : nullptr;
450 if (!isa_and_nonnull<AddrLabelExpr>(A) ||
451 !isa_and_nonnull<AddrLabelExpr>(B))
452 return false;
453 const auto *LHSAddrExpr = cast<AddrLabelExpr>(A);
454 const auto *RHSAddrExpr = cast<AddrLabelExpr>(B);
455
456 if (LHSAddrExpr->getLabel()->getDeclContext() !=
457 RHSAddrExpr->getLabel()->getDeclContext())
458 return Invalid(S, OpPC);
459
460 S.Stk.push<T>(LHSAddrExpr, RHSAddrExpr);
461 return true;
462 }
463
464 if (!LHS.isNumber() && RHS.isNumber())
465 return AddSubNonNumber<T, std::minus>(S, OpPC, LHS, RHS);
466 else if (LHS.isNumber() && RHS.isNumber())
467 ; // Fall through to proper addition below.
468 else
469 return false; // Reject everything else.
470 }
471
472 return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS);
473}
474
475inline bool Subf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
476 const Floating &RHS = S.Stk.pop<Floating>();
477 const Floating &LHS = S.Stk.pop<Floating>();
478
479 FPOptions FPO = FPOptions::getFromOpaqueInt(Value: FPOI);
480 Floating Result = S.allocFloat(Sem: LHS.getSemantics());
481 auto Status = Floating::sub(A: LHS, B: RHS, RM: getRoundingMode(FPO), R: &Result);
482 S.Stk.push<Floating>(Args&: Result);
483 return CheckFloatResult(S, OpPC, Result, Status, FPO);
484}
485
486template <PrimType Name, class T = typename PrimConv<Name>::T>
487bool Mul(InterpState &S, CodePtr OpPC) {
488 const T &RHS = S.Stk.pop<T>();
489 const T &LHS = S.Stk.pop<T>();
490 const unsigned Bits = RHS.bitWidth() * 2;
491
492 if constexpr (isIntegralOrPointer<T>()) {
493 if (!LHS.isNumber() || !RHS.isNumber())
494 return Invalid(S, OpPC);
495 }
496
497 return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
498}
499
500inline bool Mulf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
501 const Floating &RHS = S.Stk.pop<Floating>();
502 const Floating &LHS = S.Stk.pop<Floating>();
503
504 FPOptions FPO = FPOptions::getFromOpaqueInt(Value: FPOI);
505 Floating Result = S.allocFloat(Sem: LHS.getSemantics());
506
507 auto Status = Floating::mul(A: LHS, B: RHS, RM: getRoundingMode(FPO), R: &Result);
508
509 S.Stk.push<Floating>(Args&: Result);
510 return CheckFloatResult(S, OpPC, Result, Status, FPO);
511}
512
513template <PrimType Name, class T = typename PrimConv<Name>::T>
514inline bool Mulc(InterpState &S) {
515 const Pointer &RHS = S.Stk.pop<Pointer>();
516 const Pointer &LHS = S.Stk.pop<Pointer>();
517 const Pointer &Result = S.Stk.peek<Pointer>();
518
519 if constexpr (std::is_same_v<T, Floating>) {
520 APFloat A = LHS.elem<Floating>(I: 0).getAPFloat();
521 APFloat B = LHS.elem<Floating>(I: 1).getAPFloat();
522 APFloat C = RHS.elem<Floating>(I: 0).getAPFloat();
523 APFloat D = RHS.elem<Floating>(I: 1).getAPFloat();
524
525 APFloat ResR(A.getSemantics());
526 APFloat ResI(A.getSemantics());
527 HandleComplexComplexMul(A, B, C, D, ResR, ResI);
528
529 // Copy into the result.
530 Floating RA = S.allocFloat(Sem: A.getSemantics());
531 RA.copy(F: ResR);
532 Result.elem<Floating>(I: 0) = RA; // Floating(ResR);
533
534 Floating RI = S.allocFloat(Sem: A.getSemantics());
535 RI.copy(F: ResI);
536 Result.elem<Floating>(I: 1) = RI; // Floating(ResI);
537 Result.initializeAllElements();
538 } else {
539 // Integer element type.
540 const T &LHSR = LHS.elem<T>(0);
541 const T &LHSI = LHS.elem<T>(1);
542 const T &RHSR = RHS.elem<T>(0);
543 const T &RHSI = RHS.elem<T>(1);
544 unsigned Bits = LHSR.bitWidth();
545
546 // We only handle actual numbers here.
547 if (!LHSR.isNumber() || !LHSI.isNumber() || !RHSR.isNumber() ||
548 !RHSI.isNumber())
549 return false;
550
551 // real(Result) = (real(LHS) * real(RHS)) - (imag(LHS) * imag(RHS))
552 T A;
553 if constexpr (needsAlloc<T>())
554 A = S.allocAP<T>(Bits);
555 if (T::mul(LHSR, RHSR, Bits, &A))
556 return false;
557
558 T B;
559 if constexpr (needsAlloc<T>())
560 B = S.allocAP<T>(Bits);
561 if (T::mul(LHSI, RHSI, Bits, &B))
562 return false;
563
564 if constexpr (needsAlloc<T>())
565 Result.elem<T>(0) = S.allocAP<T>(Bits);
566 if (T::sub(A, B, Bits, &Result.elem<T>(0)))
567 return false;
568
569 // imag(Result) = (real(LHS) * imag(RHS)) + (imag(LHS) * real(RHS))
570 if (T::mul(LHSR, RHSI, Bits, &A))
571 return false;
572 if (T::mul(LHSI, RHSR, Bits, &B))
573 return false;
574
575 if constexpr (needsAlloc<T>())
576 Result.elem<T>(1) = S.allocAP<T>(Bits);
577 if (T::add(A, B, Bits, &Result.elem<T>(1)))
578 return false;
579 Result.initialize();
580 Result.initializeAllElements();
581 }
582
583 return true;
584}
585
586template <PrimType Name, class T = typename PrimConv<Name>::T>
587inline bool Divc(InterpState &S, CodePtr OpPC) {
588 const Pointer &RHS = S.Stk.pop<Pointer>();
589 const Pointer &LHS = S.Stk.pop<Pointer>();
590 const Pointer &Result = S.Stk.peek<Pointer>();
591
592 if constexpr (std::is_same_v<T, Floating>) {
593 APFloat A = LHS.elem<Floating>(I: 0).getAPFloat();
594 APFloat B = LHS.elem<Floating>(I: 1).getAPFloat();
595 APFloat C = RHS.elem<Floating>(I: 0).getAPFloat();
596 APFloat D = RHS.elem<Floating>(I: 1).getAPFloat();
597
598 APFloat ResR(A.getSemantics());
599 APFloat ResI(A.getSemantics());
600 HandleComplexComplexDiv(A, B, C, D, ResR, ResI);
601
602 // Copy into the result.
603 Floating RA = S.allocFloat(Sem: A.getSemantics());
604 RA.copy(F: ResR);
605 Result.elem<Floating>(I: 0) = RA; // Floating(ResR);
606
607 Floating RI = S.allocFloat(Sem: A.getSemantics());
608 RI.copy(F: ResI);
609 Result.elem<Floating>(I: 1) = RI; // Floating(ResI);
610
611 Result.initializeAllElements();
612 } else {
613 // Integer element type.
614 const T &LHSR = LHS.elem<T>(0);
615 const T &LHSI = LHS.elem<T>(1);
616 const T &RHSR = RHS.elem<T>(0);
617 const T &RHSI = RHS.elem<T>(1);
618 unsigned Bits = LHSR.bitWidth();
619
620 if (RHSR.isZero() && RHSI.isZero()) {
621 const SourceInfo &E = S.Current->getSource(PC: OpPC);
622 S.FFDiag(SI: E, DiagId: diag::note_expr_divide_by_zero);
623 return false;
624 }
625
626 // Den = real(RHS)² + imag(RHS)²
627 T A, B;
628 if constexpr (needsAlloc<T>()) {
629 A = S.allocAP<T>(Bits);
630 B = S.allocAP<T>(Bits);
631 }
632
633 if (T::mul(RHSR, RHSR, Bits, &A) || T::mul(RHSI, RHSI, Bits, &B)) {
634 // Ignore overflow here, because that's what the current interpeter does.
635 }
636 T Den;
637 if constexpr (needsAlloc<T>())
638 Den = S.allocAP<T>(Bits);
639
640 if (T::add(A, B, Bits, &Den))
641 return false;
642
643 if (Den.isZero()) {
644 const SourceInfo &E = S.Current->getSource(PC: OpPC);
645 S.FFDiag(SI: E, DiagId: diag::note_expr_divide_by_zero);
646 return false;
647 }
648
649 // real(Result) = ((real(LHS) * real(RHS)) + (imag(LHS) * imag(RHS))) / Den
650 T &ResultR = Result.elem<T>(0);
651 T &ResultI = Result.elem<T>(1);
652 if constexpr (needsAlloc<T>()) {
653 ResultR = S.allocAP<T>(Bits);
654 ResultI = S.allocAP<T>(Bits);
655 }
656 if (T::mul(LHSR, RHSR, Bits, &A) || T::mul(LHSI, RHSI, Bits, &B))
657 return false;
658 if (T::add(A, B, Bits, &ResultR))
659 return false;
660 if (T::div(ResultR, Den, Bits, &ResultR))
661 return false;
662
663 // imag(Result) = ((imag(LHS) * real(RHS)) - (real(LHS) * imag(RHS))) / Den
664 if (T::mul(LHSI, RHSR, Bits, &A) || T::mul(LHSR, RHSI, Bits, &B))
665 return false;
666 if (T::sub(A, B, Bits, &ResultI))
667 return false;
668 if (T::div(ResultI, Den, Bits, &ResultI))
669 return false;
670 Result.initializeAllElements();
671 }
672
673 return true;
674}
675
676/// 1) Pops the RHS from the stack.
677/// 2) Pops the LHS from the stack.
678/// 3) Pushes 'LHS & RHS' on the stack
679template <PrimType Name, class T = typename PrimConv<Name>::T>
680bool BitAnd(InterpState &S) {
681 const T &RHS = S.Stk.pop<T>();
682 const T &LHS = S.Stk.pop<T>();
683 unsigned Bits = RHS.bitWidth();
684
685 if constexpr (isIntegralOrPointer<T>()) {
686 if (!LHS.isNumber() || !RHS.isNumber())
687 return false;
688 }
689
690 T Result;
691 if constexpr (needsAlloc<T>())
692 Result = S.allocAP<T>(Bits);
693
694 if (!T::bitAnd(LHS, RHS, Bits, &Result)) {
695 S.Stk.push<T>(Result);
696 return true;
697 }
698 return false;
699}
700
701/// 1) Pops the RHS from the stack.
702/// 2) Pops the LHS from the stack.
703/// 3) Pushes 'LHS | RHS' on the stack
704template <PrimType Name, class T = typename PrimConv<Name>::T>
705bool BitOr(InterpState &S) {
706 const T &RHS = S.Stk.pop<T>();
707 const T &LHS = S.Stk.pop<T>();
708 unsigned Bits = RHS.bitWidth();
709
710 if constexpr (isIntegralOrPointer<T>()) {
711 if (!LHS.isNumber() || !RHS.isNumber())
712 return false;
713 }
714
715 T Result;
716 if constexpr (needsAlloc<T>())
717 Result = S.allocAP<T>(Bits);
718
719 if (!T::bitOr(LHS, RHS, Bits, &Result)) {
720 S.Stk.push<T>(Result);
721 return true;
722 }
723 return false;
724}
725
726/// 1) Pops the RHS from the stack.
727/// 2) Pops the LHS from the stack.
728/// 3) Pushes 'LHS ^ RHS' on the stack
729template <PrimType Name, class T = typename PrimConv<Name>::T>
730bool BitXor(InterpState &S) {
731 const T &RHS = S.Stk.pop<T>();
732 const T &LHS = S.Stk.pop<T>();
733 unsigned Bits = RHS.bitWidth();
734
735 if constexpr (isIntegralOrPointer<T>()) {
736 if (!LHS.isNumber() || !RHS.isNumber())
737 return false;
738 }
739
740 T Result;
741 if constexpr (needsAlloc<T>())
742 Result = S.allocAP<T>(Bits);
743
744 if (!T::bitXor(LHS, RHS, Bits, &Result)) {
745 S.Stk.push<T>(Result);
746 return true;
747 }
748 return false;
749}
750
751/// 1) Pops the RHS from the stack.
752/// 2) Pops the LHS from the stack.
753/// 3) Pushes 'LHS % RHS' on the stack (the remainder of dividing LHS by RHS).
754template <PrimType Name, class T = typename PrimConv<Name>::T>
755bool Rem(InterpState &S, CodePtr OpPC) {
756 const T &RHS = S.Stk.pop<T>();
757 const T &LHS = S.Stk.pop<T>();
758 const unsigned Bits = RHS.bitWidth() * 2;
759
760 if (!CheckDivRem(S, OpPC, LHS, RHS))
761 return false;
762
763 T Result;
764 if constexpr (needsAlloc<T>())
765 Result = S.allocAP<T>(LHS.bitWidth());
766
767 if (!T::rem(LHS, RHS, Bits, &Result)) {
768 S.Stk.push<T>(Result);
769 return true;
770 }
771 return false;
772}
773
774/// 1) Pops the RHS from the stack.
775/// 2) Pops the LHS from the stack.
776/// 3) Pushes 'LHS / RHS' on the stack
777template <PrimType Name, class T = typename PrimConv<Name>::T>
778bool Div(InterpState &S, CodePtr OpPC) {
779 const T &RHS = S.Stk.pop<T>();
780 const T &LHS = S.Stk.pop<T>();
781 const unsigned Bits = RHS.bitWidth() * 2;
782
783 if (!CheckDivRem(S, OpPC, LHS, RHS))
784 return false;
785
786 T Result;
787 if constexpr (needsAlloc<T>())
788 Result = S.allocAP<T>(LHS.bitWidth());
789
790 if (!T::div(LHS, RHS, Bits, &Result)) {
791 S.Stk.push<T>(Result);
792 return true;
793 }
794
795 if constexpr (std::is_same_v<T, FixedPoint>) {
796 if (handleFixedPointOverflow(S, OpPC, Result)) {
797 S.Stk.push<T>(Result);
798 return true;
799 }
800 }
801 return false;
802}
803
804inline bool Divf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
805 const Floating &RHS = S.Stk.pop<Floating>();
806 const Floating &LHS = S.Stk.pop<Floating>();
807
808 if (!CheckDivRem(S, OpPC, LHS, RHS))
809 return false;
810
811 FPOptions FPO = FPOptions::getFromOpaqueInt(Value: FPOI);
812
813 Floating Result = S.allocFloat(Sem: LHS.getSemantics());
814 auto Status = Floating::div(A: LHS, B: RHS, RM: getRoundingMode(FPO), R: &Result);
815
816 S.Stk.push<Floating>(Args&: Result);
817 return CheckFloatResult(S, OpPC, Result, Status, FPO);
818}
819
820//===----------------------------------------------------------------------===//
821// Inv
822//===----------------------------------------------------------------------===//
823
824inline bool Inv(InterpState &S) {
825 const auto &Val = S.Stk.pop<Boolean>();
826 S.Stk.push<Boolean>(Args: !Val);
827 return true;
828}
829
830//===----------------------------------------------------------------------===//
831// Neg
832//===----------------------------------------------------------------------===//
833
834template <PrimType Name, class T = typename PrimConv<Name>::T>
835bool Neg(InterpState &S, CodePtr OpPC) {
836 const T &Value = S.Stk.pop<T>();
837
838 if constexpr (std::is_same_v<T, Floating>) {
839 T Result = S.allocFloat(Sem: Value.getSemantics());
840
841 if (!T::neg(Value, &Result)) {
842 S.Stk.push<T>(Result);
843 return true;
844 }
845 return false;
846 } else {
847 T Result;
848 if constexpr (needsAlloc<T>())
849 Result = S.allocAP<T>(Value.bitWidth());
850
851 if (!T::neg(Value, &Result)) {
852 S.Stk.push<T>(Result);
853 return true;
854 }
855
856 assert((isIntegerType(Name) || Name == PT_FixedPoint) &&
857 "don't expect other types to fail at constexpr negation");
858 S.Stk.push<T>(Result);
859
860 if (S.Current->getExpr(PC: OpPC)->getType().isWrapType())
861 return true;
862
863 APSInt NegatedValue = -Value.toAPSInt(Value.bitWidth() + 1);
864 if (S.checkingForUndefinedBehavior()) {
865 const Expr *E = S.Current->getExpr(PC: OpPC);
866 QualType Type = E->getType();
867 SmallString<32> Trunc;
868 NegatedValue.trunc(width: Result.bitWidth())
869 .toString(Trunc, 10, Result.isSigned(), /*formatAsCLiteral=*/false,
870 /*UpperCase=*/true, /*InsertSeparators=*/true);
871 S.report(Loc: E->getExprLoc(), DiagId: diag::warn_integer_constant_overflow)
872 << Trunc << Type << E->getSourceRange();
873 return true;
874 }
875
876 return handleOverflow(S, OpPC, SrcValue: NegatedValue);
877 }
878}
879
880enum class PushVal : bool {
881 No,
882 Yes,
883};
884enum class IncDecOp {
885 Inc,
886 Dec,
887};
888
889template <typename T, IncDecOp Op, PushVal DoPush>
890bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
891 bool CanOverflow, UnsignedOrNone BitWidth = std::nullopt) {
892 assert(!Ptr.isDummy());
893
894 if (!S.inConstantContext()) {
895 if (isConstexprUnknown(P: Ptr))
896 return false;
897 }
898
899 if constexpr (std::is_same_v<T, Boolean>) {
900 if (!S.getLangOpts().CPlusPlus14)
901 return Invalid(S, OpPC);
902 }
903
904 const T &Value = Ptr.deref<T>();
905
906 // Can't inc/dec non-numbers.
907 if constexpr (isIntegralOrPointer<T>()) {
908 if (!Value.isNumber())
909 return false;
910 }
911
912 T Result;
913 if constexpr (needsAlloc<T>())
914 Result = S.allocAP<T>(Value.bitWidth());
915
916 if constexpr (DoPush == PushVal::Yes)
917 S.Stk.push<T>(Value);
918
919 if constexpr (Op == IncDecOp::Inc) {
920 if (!T::increment(Value, &Result) || !CanOverflow) {
921 if (BitWidth)
922 Ptr.deref<T>() = Result.truncate(*BitWidth);
923 else
924 Ptr.deref<T>() = Result;
925 return true;
926 }
927 } else {
928 if (!T::decrement(Value, &Result) || !CanOverflow) {
929 if (BitWidth)
930 Ptr.deref<T>() = Result.truncate(*BitWidth);
931 else
932 Ptr.deref<T>() = Result;
933 return true;
934 }
935 }
936 assert(CanOverflow);
937
938 if (S.Current->getExpr(PC: OpPC)->getType().isWrapType()) {
939 Ptr.deref<T>() = Result;
940 return true;
941 }
942
943 // Something went wrong with the previous operation. Compute the
944 // result with another bit of precision.
945 unsigned Bits = Value.bitWidth() + 1;
946 APSInt APResult;
947 if constexpr (Op == IncDecOp::Inc)
948 APResult = ++Value.toAPSInt(Bits);
949 else
950 APResult = --Value.toAPSInt(Bits);
951
952 // Report undefined behaviour, stopping if required.
953 if (S.checkingForUndefinedBehavior()) {
954 const Expr *E = S.Current->getExpr(PC: OpPC);
955 QualType Type = E->getType();
956 SmallString<32> Trunc;
957 APResult.trunc(width: Result.bitWidth())
958 .toString(Trunc, 10, Result.isSigned(), /*formatAsCLiteral=*/false,
959 /*UpperCase=*/true, /*InsertSeparators=*/true);
960 S.report(Loc: E->getExprLoc(), DiagId: diag::warn_integer_constant_overflow)
961 << Trunc << Type << E->getSourceRange();
962 return true;
963 }
964 return handleOverflow(S, OpPC, SrcValue: APResult);
965}
966
967/// 1) Pops a pointer from the stack
968/// 2) Load the value from the pointer
969/// 3) Writes the value increased by one back to the pointer
970/// 4) Pushes the original (pre-inc) value on the stack.
971template <PrimType Name, class T = typename PrimConv<Name>::T>
972bool Inc(InterpState &S, CodePtr OpPC, bool CanOverflow) {
973 const Pointer &Ptr = S.Stk.pop<Pointer>();
974 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Increment))
975 return false;
976 if (!CheckConst(S, OpPC, Ptr))
977 return false;
978
979 return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr,
980 CanOverflow);
981}
982
983template <PrimType Name, class T = typename PrimConv<Name>::T>
984bool IncBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
985 unsigned BitWidth) {
986 const Pointer &Ptr = S.Stk.pop<Pointer>();
987 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Increment))
988 return false;
989 if (!CheckConst(S, OpPC, Ptr))
990 return false;
991
992 return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr, CanOverflow,
993 BitWidth);
994}
995
996/// 1) Pops a pointer from the stack
997/// 2) Load the value from the pointer
998/// 3) Writes the value increased by one back to the pointer
999template <PrimType Name, class T = typename PrimConv<Name>::T>
1000bool IncPop(InterpState &S, CodePtr OpPC, bool CanOverflow) {
1001 const Pointer &Ptr = S.Stk.pop<Pointer>();
1002 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Increment))
1003 return false;
1004 if (!CheckConst(S, OpPC, Ptr))
1005 return false;
1006
1007 return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow);
1008}
1009
1010template <PrimType Name, class T = typename PrimConv<Name>::T>
1011bool IncPopBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
1012 uint32_t BitWidth) {
1013 const Pointer &Ptr = S.Stk.pop<Pointer>();
1014 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Increment))
1015 return false;
1016 if (!CheckConst(S, OpPC, Ptr))
1017 return false;
1018
1019 return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow,
1020 BitWidth);
1021}
1022
1023template <PrimType Name, class T = typename PrimConv<Name>::T>
1024bool PreInc(InterpState &S, CodePtr OpPC, bool CanOverflow) {
1025 const Pointer &Ptr = S.Stk.peek<Pointer>();
1026 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Increment))
1027 return false;
1028 if (!CheckConst(S, OpPC, Ptr))
1029 return false;
1030
1031 return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow);
1032}
1033
1034template <PrimType Name, class T = typename PrimConv<Name>::T>
1035bool PreIncBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
1036 uint32_t BitWidth) {
1037 const Pointer &Ptr = S.Stk.peek<Pointer>();
1038 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Increment))
1039 return false;
1040 if (!CheckConst(S, OpPC, Ptr))
1041 return false;
1042
1043 return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow,
1044 BitWidth);
1045}
1046
1047/// 1) Pops a pointer from the stack
1048/// 2) Load the value from the pointer
1049/// 3) Writes the value decreased by one back to the pointer
1050/// 4) Pushes the original (pre-dec) value on the stack.
1051template <PrimType Name, class T = typename PrimConv<Name>::T>
1052bool Dec(InterpState &S, CodePtr OpPC, bool CanOverflow) {
1053 const Pointer &Ptr = S.Stk.pop<Pointer>();
1054 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Decrement))
1055 return false;
1056 if (!CheckConst(S, OpPC, Ptr))
1057 return false;
1058
1059 return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr,
1060 CanOverflow);
1061}
1062template <PrimType Name, class T = typename PrimConv<Name>::T>
1063bool DecBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
1064 uint32_t BitWidth) {
1065 const Pointer &Ptr = S.Stk.pop<Pointer>();
1066 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Decrement))
1067 return false;
1068 if (!CheckConst(S, OpPC, Ptr))
1069 return false;
1070
1071 return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr, CanOverflow,
1072 BitWidth);
1073}
1074
1075/// 1) Pops a pointer from the stack
1076/// 2) Load the value from the pointer
1077/// 3) Writes the value decreased by one back to the pointer
1078template <PrimType Name, class T = typename PrimConv<Name>::T>
1079bool DecPop(InterpState &S, CodePtr OpPC, bool CanOverflow) {
1080 const Pointer &Ptr = S.Stk.pop<Pointer>();
1081 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Decrement))
1082 return false;
1083 if (!CheckConst(S, OpPC, Ptr))
1084 return false;
1085
1086 return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow);
1087}
1088
1089template <PrimType Name, class T = typename PrimConv<Name>::T>
1090bool DecPopBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
1091 uint32_t BitWidth) {
1092 const Pointer &Ptr = S.Stk.pop<Pointer>();
1093 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Decrement))
1094 return false;
1095 if (!CheckConst(S, OpPC, Ptr))
1096 return false;
1097
1098 return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow,
1099 BitWidth);
1100}
1101
1102template <PrimType Name, class T = typename PrimConv<Name>::T>
1103bool PreDec(InterpState &S, CodePtr OpPC, bool CanOverflow) {
1104 const Pointer &Ptr = S.Stk.peek<Pointer>();
1105 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Decrement))
1106 return false;
1107 if (!CheckConst(S, OpPC, Ptr))
1108 return false;
1109 return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow);
1110}
1111
1112template <PrimType Name, class T = typename PrimConv<Name>::T>
1113bool PreDecBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
1114 uint32_t BitWidth) {
1115 const Pointer &Ptr = S.Stk.peek<Pointer>();
1116 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Decrement))
1117 return false;
1118 if (!CheckConst(S, OpPC, Ptr))
1119 return false;
1120 return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow,
1121 BitWidth);
1122}
1123
1124template <IncDecOp Op, PushVal DoPush>
1125bool IncDecFloatHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
1126 uint32_t FPOI) {
1127 Floating Value = Ptr.deref<Floating>();
1128 Floating Result = S.allocFloat(Sem: Value.getSemantics());
1129
1130 if constexpr (DoPush == PushVal::Yes)
1131 S.Stk.push<Floating>(Args&: Value);
1132
1133 FPOptions FPO = FPOptions::getFromOpaqueInt(Value: FPOI);
1134 llvm::APFloat::opStatus Status;
1135 if constexpr (Op == IncDecOp::Inc)
1136 Status = Floating::increment(A: Value, RM: getRoundingMode(FPO), R: &Result);
1137 else
1138 Status = Floating::decrement(A: Value, RM: getRoundingMode(FPO), R: &Result);
1139
1140 Ptr.deref<Floating>() = Result;
1141
1142 return CheckFloatResult(S, OpPC, Result, Status, FPO);
1143}
1144
1145inline bool Incf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
1146 const Pointer &Ptr = S.Stk.pop<Pointer>();
1147 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Increment))
1148 return false;
1149 if (!CheckConst(S, OpPC, Ptr))
1150 return false;
1151
1152 return IncDecFloatHelper<IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr, FPOI);
1153}
1154
1155inline bool IncfPop(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
1156 const Pointer &Ptr = S.Stk.pop<Pointer>();
1157 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Increment))
1158 return false;
1159 if (!CheckConst(S, OpPC, Ptr))
1160 return false;
1161
1162 return IncDecFloatHelper<IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, FPOI);
1163}
1164
1165inline bool Decf(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
1166 const Pointer &Ptr = S.Stk.pop<Pointer>();
1167 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Decrement))
1168 return false;
1169 if (!CheckConst(S, OpPC, Ptr))
1170 return false;
1171
1172 return IncDecFloatHelper<IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr, FPOI);
1173}
1174
1175inline bool DecfPop(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
1176 const Pointer &Ptr = S.Stk.pop<Pointer>();
1177 if (!CheckLoad(S, OpPC, Ptr, AK: AK_Decrement))
1178 return false;
1179 if (!CheckConst(S, OpPC, Ptr))
1180 return false;
1181
1182 return IncDecFloatHelper<IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, FPOI);
1183}
1184
1185/// 1) Pops the value from the stack.
1186/// 2) Pushes the bitwise complemented value on the stack (~V).
1187template <PrimType Name, class T = typename PrimConv<Name>::T>
1188bool Comp(InterpState &S) {
1189 const T &Val = S.Stk.pop<T>();
1190
1191 T Result;
1192 if constexpr (needsAlloc<T>())
1193 Result = S.allocAP<T>(Val.bitWidth());
1194
1195 if (!T::comp(Val, &Result)) {
1196 S.Stk.push<T>(Result);
1197 return true;
1198 }
1199 return false;
1200}
1201
1202//===----------------------------------------------------------------------===//
1203// EQ, NE, GT, GE, LT, LE
1204//===----------------------------------------------------------------------===//
1205
1206using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>;
1207
1208template <typename T>
1209bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) {
1210 assert((!std::is_same_v<T, MemberPointer>) &&
1211 "Non-equality comparisons on member pointer types should already be "
1212 "rejected in Sema.");
1213 using BoolT = PrimConv<PT_Bool>::T;
1214 const T &RHS = S.Stk.pop<T>();
1215 const T &LHS = S.Stk.pop<T>();
1216
1217 if constexpr (isIntegralOrPointer<T>()) {
1218 if (!LHS.isNumber() || !RHS.isNumber())
1219 return Invalid(S, OpPC);
1220 }
1221
1222 S.Stk.push<BoolT>(BoolT::from(Fn(LHS.compare(RHS))));
1223 return true;
1224}
1225
1226template <typename T>
1227bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) {
1228 return CmpHelper<T>(S, OpPC, Fn);
1229}
1230
1231template <>
1232inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
1233 using BoolT = PrimConv<PT_Bool>::T;
1234 const Pointer &RHS = S.Stk.pop<Pointer>();
1235 const Pointer &LHS = S.Stk.pop<Pointer>();
1236
1237 // Function pointers cannot be compared in an ordered way.
1238 if (LHS.isFunctionPointer() || RHS.isFunctionPointer() ||
1239 LHS.isTypeidPointer() || RHS.isTypeidPointer()) {
1240 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1241 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_pointer_comparison_unspecified)
1242 << LHS.toDiagnosticString(Ctx: S.getASTContext())
1243 << RHS.toDiagnosticString(Ctx: S.getASTContext());
1244 return false;
1245 }
1246
1247 if (LHS == RHS) {
1248 S.Stk.push<BoolT>(Args: BoolT::from(Value: Fn(ComparisonCategoryResult::Equal)));
1249 return true;
1250 }
1251
1252 if (!Pointer::hasSameBase(A: LHS, B: RHS)) {
1253 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1254 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_pointer_comparison_unspecified)
1255 << LHS.toDiagnosticString(Ctx: S.getASTContext())
1256 << RHS.toDiagnosticString(Ctx: S.getASTContext());
1257 return false;
1258 }
1259
1260 // Diagnose comparisons between fields with different access specifiers,
1261 // comparisons between bases and bases+fields.
1262 if (std::optional<std::pair<PtrView, PtrView>> Split =
1263 Pointer::computeSplitPoint(A: LHS, B: RHS)) {
1264 const FieldDecl *LF = Split->first.getField();
1265 const FieldDecl *RF = Split->second.getField();
1266 if (!LF && !RF)
1267 S.CCEDiag(SI: S.Current->getSource(PC: OpPC),
1268 DiagId: diag::note_constexpr_pointer_comparison_base_classes);
1269 else if (!LF)
1270 S.CCEDiag(SI: S.Current->getSource(PC: OpPC),
1271 DiagId: diag::note_constexpr_pointer_comparison_base_field)
1272 << Split->first.getRecord()->getDecl() << RF->getParent() << RF;
1273 else if (!RF)
1274 S.CCEDiag(SI: S.Current->getSource(PC: OpPC),
1275 DiagId: diag::note_constexpr_pointer_comparison_base_field)
1276 << Split->second.getRecord()->getDecl() << LF->getParent() << LF;
1277 else if (!LF->getParent()->isUnion() &&
1278 LF->getAccess() != RF->getAccess()) {
1279 S.CCEDiag(SI: S.Current->getSource(PC: OpPC),
1280 DiagId: diag::note_constexpr_pointer_comparison_differing_access)
1281 << LF << LF->getAccess() << RF << RF->getAccess() << LF->getParent();
1282 }
1283 }
1284
1285 std::optional<size_t> VL = LHS.computeOffsetForComparison(ASTCtx: S.getASTContext());
1286 std::optional<size_t> VR = RHS.computeOffsetForComparison(ASTCtx: S.getASTContext());
1287 if (!VL || !VR)
1288 return Invalid(S, OpPC);
1289 S.Stk.push<BoolT>(Args: BoolT::from(Value: Fn(Compare(X: *VL, Y: *VR))));
1290 return true;
1291}
1292
1293static inline bool IsOpaqueConstantCall(const CallExpr *E) {
1294 unsigned Builtin = E->getBuiltinCallee();
1295 return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
1296 Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
1297 Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
1298 Builtin == Builtin::BI__builtin_function_start);
1299}
1300
1301bool arePotentiallyOverlappingStringLiterals(const Pointer &LHS,
1302 const Pointer &RHS);
1303
1304template <>
1305inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
1306 using BoolT = PrimConv<PT_Bool>::T;
1307 const Pointer &RHS = S.Stk.pop<Pointer>();
1308 const Pointer &LHS = S.Stk.pop<Pointer>();
1309
1310 if (LHS.isZero() && RHS.isZero()) {
1311 S.Stk.push<BoolT>(Args: BoolT::from(Value: Fn(ComparisonCategoryResult::Equal)));
1312 return true;
1313 }
1314
1315 // Reject comparisons to weak pointers.
1316 for (const auto &P : {LHS, RHS}) {
1317 if (P.isZero())
1318 continue;
1319 if (P.isWeak()) {
1320 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1321 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_pointer_weak_comparison)
1322 << P.toDiagnosticString(Ctx: S.getASTContext());
1323 return false;
1324 }
1325 }
1326
1327 // p == nullptr or nullptr == p.
1328 if (RHS.isZero() || LHS.isZero()) {
1329 S.Stk.push<BoolT>(Args: BoolT::from(Value: Fn(ComparisonCategoryResult::Unordered)));
1330 return true;
1331 }
1332
1333 assert(!LHS.isZero());
1334 assert(!RHS.isZero());
1335
1336 if (!S.inConstantContext()) {
1337 if (isConstexprUnknown(P: LHS) || isConstexprUnknown(P: RHS))
1338 return false;
1339 }
1340
1341 if (LHS.isFunctionPointer() && RHS.isFunctionPointer()) {
1342 S.Stk.push<BoolT>(Args: BoolT::from(Value: Fn(Compare(X: LHS.getIntegerRepresentation(),
1343 Y: RHS.getIntegerRepresentation()))));
1344 return true;
1345 }
1346
1347 if (Pointer::hasSameBase(A: LHS, B: RHS)) {
1348 std::optional<size_t> A = LHS.computeOffsetForComparison(ASTCtx: S.getASTContext());
1349 std::optional<size_t> B = RHS.computeOffsetForComparison(ASTCtx: S.getASTContext());
1350 if (!A || !B)
1351 return Invalid(S, OpPC);
1352
1353 S.Stk.push<BoolT>(Args: BoolT::from(Value: Fn(Compare(X: *A, Y: *B))));
1354 return true;
1355 }
1356 // Otherwise we need to do a bunch of extra checks before returning Unordered.
1357
1358 if (LHS.isStringPointer() && RHS.isStringPointer() &&
1359 arePotentiallyOverlappingStringLiterals(LHS, RHS)) {
1360 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
1361 DiagId: diag::note_constexpr_literal_comparison)
1362 << LHS.toDiagnosticString(Ctx: S.getASTContext())
1363 << RHS.toDiagnosticString(Ctx: S.getASTContext());
1364 return false;
1365 }
1366
1367 if (LHS.isOnePastEnd() && !RHS.isOnePastEnd()) {
1368 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1369 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_pointer_comparison_past_end)
1370 << LHS.toDiagnosticString(Ctx: S.getASTContext());
1371 return false;
1372 }
1373 if (RHS.isOnePastEnd() && !LHS.isOnePastEnd()) {
1374 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1375 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_pointer_comparison_past_end)
1376 << RHS.toDiagnosticString(Ctx: S.getASTContext());
1377 return false;
1378 }
1379
1380 // Reject comparisons to literals.
1381 for (const auto &P : {LHS, RHS}) {
1382 if (P.isZero())
1383 continue;
1384 if (P.pointsToLiteral()) {
1385 const Expr *E = P.getRootExpr();
1386 if (isa<StringLiteral>(Val: E)) {
1387 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1388 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_literal_comparison);
1389 return false;
1390 }
1391 if (const auto *CE = dyn_cast<CallExpr>(Val: E);
1392 CE && IsOpaqueConstantCall(E: CE)) {
1393 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1394 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_opaque_call_comparison)
1395 << P.toDiagnosticString(Ctx: S.getASTContext());
1396 return false;
1397 }
1398 } else if (P.isIntegralPointer()) {
1399 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1400 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_pointer_constant_comparison)
1401 << LHS.toDiagnosticString(Ctx: S.getASTContext())
1402 << RHS.toDiagnosticString(Ctx: S.getASTContext());
1403 return false;
1404 }
1405 }
1406
1407 if (LHS.isUnknownSizeArray() && RHS.isUnknownSizeArray()) {
1408 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1409 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_pointer_comparison_zero_sized)
1410 << LHS.toDiagnosticString(Ctx: S.getASTContext())
1411 << RHS.toDiagnosticString(Ctx: S.getASTContext());
1412 return false;
1413 }
1414
1415 if (LHS.isConstexprUnknown() || RHS.isConstexprUnknown()) {
1416 if (!S.checkingPotentialConstantExpression())
1417 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
1418 DiagId: diag::note_constexpr_pointer_comparison_unspecified)
1419 << LHS.toDiagnosticString(Ctx: S.getASTContext())
1420 << RHS.toDiagnosticString(Ctx: S.getASTContext());
1421 return false;
1422 }
1423
1424 S.Stk.push<BoolT>(Args: BoolT::from(Value: Fn(ComparisonCategoryResult::Unordered)));
1425 return true;
1426}
1427
1428template <>
1429inline bool CmpHelperEQ<MemberPointer>(InterpState &S, CodePtr OpPC,
1430 CompareFn Fn) {
1431 const auto &RHS = S.Stk.pop<MemberPointer>();
1432 const auto &LHS = S.Stk.pop<MemberPointer>();
1433
1434 // If either operand is a pointer to a weak function, the comparison is not
1435 // constant.
1436 for (const auto &MP : {LHS, RHS}) {
1437 if (MP.isWeak()) {
1438 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1439 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_mem_pointer_weak_comparison)
1440 << MP.getMemberFunction();
1441 return false;
1442 }
1443 }
1444
1445 // C++11 [expr.eq]p2:
1446 // If both operands are null, they compare equal. Otherwise if only one is
1447 // null, they compare unequal.
1448 if (LHS.isZero() && RHS.isZero()) {
1449 S.Stk.push<Boolean>(Args: Fn(ComparisonCategoryResult::Equal));
1450 return true;
1451 }
1452 if (LHS.isZero() || RHS.isZero()) {
1453 S.Stk.push<Boolean>(Args: Fn(ComparisonCategoryResult::Unordered));
1454 return true;
1455 }
1456
1457 // We cannot compare against virtual declarations at compile time.
1458 for (const auto &MP : {LHS, RHS}) {
1459 if (const CXXMethodDecl *MD = MP.getMemberFunction();
1460 MD && MD->isVirtual()) {
1461 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
1462 S.CCEDiag(SI: Loc, DiagId: diag::note_constexpr_compare_virtual_mem_ptr) << MD;
1463 }
1464 }
1465
1466 S.Stk.push<Boolean>(Args: Boolean::from(Value: Fn(LHS.compare(RHS))));
1467 return true;
1468}
1469
1470template <PrimType Name, class T = typename PrimConv<Name>::T>
1471bool EQ(InterpState &S, CodePtr OpPC) {
1472 return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
1473 return R == ComparisonCategoryResult::Equal;
1474 });
1475}
1476
1477template <PrimType Name, class T = typename PrimConv<Name>::T>
1478bool CMP3(InterpState &S, CodePtr OpPC, const ComparisonCategoryInfo *CmpInfo) {
1479 const T &RHS = S.Stk.pop<T>();
1480 const T &LHS = S.Stk.pop<T>();
1481 const Pointer &P = S.Stk.peek<Pointer>();
1482
1483 ComparisonCategoryResult CmpResult;
1484 if constexpr (std::is_same_v<T, Pointer>) {
1485 if (!Pointer::hasSameBase(A: LHS, B: RHS)) {
1486 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
1487 DiagId: diag::note_constexpr_pointer_comparison_unspecified)
1488 << LHS.toDiagnosticString(S.getASTContext())
1489 << RHS.toDiagnosticString(S.getASTContext());
1490 return false;
1491 }
1492 std::optional<size_t> LHSOffset =
1493 LHS.computeLayoutOffset(S.getASTContext());
1494 std::optional<size_t> RHSOffset =
1495 RHS.computeLayoutOffset(S.getASTContext());
1496 if (!LHSOffset || !RHSOffset)
1497 return false;
1498
1499 if (LHSOffset < RHSOffset)
1500 CmpResult = ComparisonCategoryResult::Less;
1501 else if (LHSOffset > RHSOffset)
1502 CmpResult = ComparisonCategoryResult::Greater;
1503 else
1504 CmpResult = ComparisonCategoryResult::Equal;
1505 } else {
1506 CmpResult = LHS.compare(RHS);
1507 }
1508
1509 assert(CmpInfo);
1510 const auto *CmpValueInfo =
1511 CmpInfo->getValueInfo(ValueKind: CmpInfo->makeWeakResult(Res: CmpResult));
1512 assert(CmpValueInfo);
1513 assert(CmpValueInfo->hasValidIntValue());
1514 return SetThreeWayComparisonField(S, OpPC, Ptr: P, IntValue: CmpValueInfo->getIntValue());
1515}
1516
1517template <PrimType Name, class T = typename PrimConv<Name>::T>
1518bool NE(InterpState &S, CodePtr OpPC) {
1519 return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
1520 return R != ComparisonCategoryResult::Equal;
1521 });
1522}
1523
1524template <PrimType Name, class T = typename PrimConv<Name>::T>
1525bool LT(InterpState &S, CodePtr OpPC) {
1526 return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
1527 return R == ComparisonCategoryResult::Less;
1528 });
1529}
1530
1531template <PrimType Name, class T = typename PrimConv<Name>::T>
1532bool LE(InterpState &S, CodePtr OpPC) {
1533 return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
1534 return R == ComparisonCategoryResult::Less ||
1535 R == ComparisonCategoryResult::Equal;
1536 });
1537}
1538
1539template <PrimType Name, class T = typename PrimConv<Name>::T>
1540bool GT(InterpState &S, CodePtr OpPC) {
1541 return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
1542 return R == ComparisonCategoryResult::Greater;
1543 });
1544}
1545
1546template <PrimType Name, class T = typename PrimConv<Name>::T>
1547bool GE(InterpState &S, CodePtr OpPC) {
1548 return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
1549 return R == ComparisonCategoryResult::Greater ||
1550 R == ComparisonCategoryResult::Equal;
1551 });
1552}
1553
1554//===----------------------------------------------------------------------===//
1555// Dup, Pop, Test
1556//===----------------------------------------------------------------------===//
1557
1558template <PrimType Name, class T = typename PrimConv<Name>::T>
1559bool Dup(InterpState &S) {
1560 S.Stk.push<T>(S.Stk.peek<T>());
1561 return true;
1562}
1563
1564template <PrimType Name, class T = typename PrimConv<Name>::T>
1565bool Pop(InterpState &S) {
1566 S.Stk.discard<T>();
1567 return true;
1568}
1569
1570/// [Value1, Value2] -> [Value2, Value1]
1571template <PrimType TopName, PrimType BottomName> bool Flip(InterpState &S) {
1572 using TopT = typename PrimConv<TopName>::T;
1573 using BottomT = typename PrimConv<BottomName>::T;
1574
1575 const auto &Top = S.Stk.pop<TopT>();
1576 const auto &Bottom = S.Stk.pop<BottomT>();
1577
1578 S.Stk.push<TopT>(Top);
1579 S.Stk.push<BottomT>(Bottom);
1580
1581 return true;
1582}
1583
1584//===----------------------------------------------------------------------===//
1585// Const
1586//===----------------------------------------------------------------------===//
1587
1588template <PrimType Name, class T = typename PrimConv<Name>::T>
1589bool Const(InterpState &S, const T &Arg) {
1590 if constexpr (needsAlloc<T>()) {
1591 T Result = S.allocAP<T>(Arg.bitWidth());
1592 Result.copy(Arg.toAPSInt());
1593 S.Stk.push<T>(Result);
1594 return true;
1595 }
1596
1597 if constexpr (std::is_same_v<T, uint16_t>) {
1598 S.Stk.push<Integral<16, false>>(Integral<16, false>::from(Arg));
1599 } else if constexpr (std::is_same_v<T, int16_t>) {
1600 S.Stk.push<Integral<16, true>>(Integral<16, true>::from(Arg));
1601 } else if constexpr (std::is_same_v<T, uint32_t>) {
1602 S.Stk.push<Integral<32, false>>(Integral<32, false>::from(Arg));
1603 } else if constexpr (std::is_same_v<T, int32_t>) {
1604 S.Stk.push<Integral<32, true>>(Integral<32, true>::from(Arg));
1605 } else if constexpr (std::is_same_v<T, uint64_t>) {
1606 S.Stk.push<Integral<64, false>>(Integral<64, false>::from(Arg));
1607 } else if constexpr (std::is_same_v<T, int64_t>) {
1608 S.Stk.push<Integral<64, true>>(Integral<64, true>::from(Arg));
1609 } else {
1610 // Bool.
1611 S.Stk.push<T>(Arg);
1612 }
1613
1614 return true;
1615}
1616
1617inline bool ConstFloat(InterpState &S, const Floating &F) {
1618 Floating Result = S.allocFloat(Sem: F.getSemantics());
1619 Result.copy(F: F.getAPFloat());
1620 S.Stk.push<Floating>(Args&: Result);
1621 return true;
1622}
1623
1624//===----------------------------------------------------------------------===//
1625// Get/Set Local/Param/Global/This
1626//===----------------------------------------------------------------------===//
1627
1628template <PrimType Name, class T = typename PrimConv<Name>::T>
1629bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
1630 const Block *B = S.Current->getLocalBlock(Offset: I);
1631 if (!CheckLocalLoad(S, OpPC, B))
1632 return false;
1633 S.Stk.push<T>(B->deref<T>());
1634 return true;
1635}
1636
1637bool EndLifetime(InterpState &S, CodePtr OpPC);
1638bool PseudoDtor(InterpState &S, CodePtr OpPC);
1639bool StartThisLifetime(InterpState &S);
1640bool StartThisLifetime1(InterpState &S);
1641bool MarkDestroyed(InterpState &S, CodePtr OpPC);
1642bool DefaultInit(InterpState &S, CodePtr OpPC, const CXXConstructorDecl *Ctor);
1643
1644/// 1) Pops the value from the stack.
1645/// 2) Writes the value to the local variable with the
1646/// given offset.
1647template <PrimType Name, class T = typename PrimConv<Name>::T>
1648bool SetLocal(InterpState &S, uint32_t I) {
1649 S.Current->setLocal<T>(I, S.Stk.pop<T>());
1650 return true;
1651}
1652
1653template <PrimType Name, class T = typename PrimConv<Name>::T>
1654bool GetParam(InterpState &S, uint32_t Index) {
1655 if (S.checkingPotentialConstantExpression()) {
1656 return false;
1657 }
1658 S.Stk.push<T>(S.Current->getParam<T>(Index));
1659 return true;
1660}
1661
1662template <PrimType Name, class T = typename PrimConv<Name>::T>
1663bool SetParam(InterpState &S, uint32_t I) {
1664 S.Current->setParam<T>(I, S.Stk.pop<T>());
1665 return true;
1666}
1667
1668/// 1) Peeks a pointer on the stack
1669/// 2) Pushes the value of the pointer's field on the stack
1670template <PrimType Name, class T = typename PrimConv<Name>::T>
1671bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
1672 const Pointer &Obj = S.Stk.peek<Pointer>();
1673 if (!CheckNull(S, OpPC, Ptr: Obj, CSK: CSK_Field))
1674 return false;
1675 if (!CheckRange(S, OpPC, Ptr: Obj, CSK: CSK_Field))
1676 return false;
1677
1678 if (!Obj.isBlockPointer())
1679 return false;
1680
1681 // FIXME(postswitch): The isUnknownSizeArray() check here is only needed
1682 // to keep an invalid sample producing the same diagnostics as the current
1683 // interpreter.
1684 if (!Obj.getFieldDesc()->isRecord() && !Obj.isUnknownSizeArray())
1685 return false;
1686
1687 PtrView FieldPtr = Obj.view().atField(Offset: I);
1688 if (!CheckLoad(S, OpPC, Ptr: FieldPtr))
1689 return false;
1690 S.Stk.push<T>(FieldPtr.deref<T>());
1691 return true;
1692}
1693
1694/// 1) Pops a pointer from the stack
1695/// 2) Pushes the value of the pointer's field on the stack
1696template <PrimType Name, class T = typename PrimConv<Name>::T>
1697bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
1698 const Pointer &Obj = S.Stk.pop<Pointer>();
1699 if (!CheckNull(S, OpPC, Ptr: Obj, CSK: CSK_Field))
1700 return false;
1701 if (!CheckRange(S, OpPC, Ptr: Obj, CSK: CSK_Field))
1702 return false;
1703
1704 if (!Obj.isBlockPointer())
1705 return false;
1706
1707 // FIXME(postswitch): The isUnknownSizeArray() check here is only needed
1708 // to keep an invalid sample producing the same diagnostics as the current
1709 // interpreter.
1710 if (!Obj.getFieldDesc()->isRecord() && !Obj.isUnknownSizeArray())
1711 return false;
1712
1713 PtrView FieldPtr = Obj.view().atField(Offset: I);
1714 if (!CheckLoad(S, OpPC, Ptr: FieldPtr))
1715 return false;
1716 S.Stk.push<T>(FieldPtr.deref<T>());
1717 return true;
1718}
1719
1720template <PrimType Name, class T = typename PrimConv<Name>::T>
1721bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
1722 if (S.checkingPotentialConstantExpression())
1723 return false;
1724 if (!CheckThis(S, OpPC))
1725 return false;
1726 const Pointer &This = S.Current->getThis();
1727
1728 if (!This.isBlockPointer())
1729 return false;
1730
1731 PtrView FieldPtr = This.view().atField(Offset: I);
1732 if (!CheckLoad(S, OpPC, Ptr: FieldPtr))
1733 return false;
1734 S.Stk.push<T>(FieldPtr.deref<T>());
1735 return true;
1736}
1737
1738template <PrimType Name, class T = typename PrimConv<Name>::T>
1739bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
1740 const Block *B = S.P.getGlobal(Idx: I);
1741
1742 if (!CheckGlobalLoad(S, OpPC, B))
1743 return false;
1744
1745 S.Stk.push<T>(B->deref<T>());
1746 return true;
1747}
1748
1749/// Same as GetGlobal, but without the checks.
1750template <PrimType Name, class T = typename PrimConv<Name>::T>
1751bool GetGlobalUnchecked(InterpState &S, CodePtr OpPC, uint32_t I) {
1752 const Block *B = S.P.getGlobal(Idx: I);
1753 const auto &Desc = B->getBlockDesc<GlobalInlineDescriptor>();
1754 if (Desc.InitState != GlobalInitState::Initialized)
1755 return diagnoseUninitialized(S, OpPC, Extern: B->isExtern(), B);
1756
1757 S.Stk.push<T>(B->deref<T>());
1758 return true;
1759}
1760
1761template <PrimType Name, class T = typename PrimConv<Name>::T>
1762bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
1763 // TODO: emit warning.
1764 return false;
1765}
1766
1767template <PrimType Name, class T = typename PrimConv<Name>::T>
1768bool InitGlobal(InterpState &S, uint32_t I) {
1769 const Pointer &P = S.P.getGlobal(Idx: I);
1770
1771 P.deref<T>() = S.Stk.pop<T>();
1772
1773 if constexpr (std::is_same_v<T, Floating>) {
1774 auto &Val = P.deref<Floating>();
1775 if (!Val.singleWord()) {
1776 uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
1777 Val.take(NewMemory);
1778 }
1779
1780 } else if constexpr (std::is_same_v<T, MemberPointer>) {
1781 auto &Val = P.deref<MemberPointer>();
1782 unsigned PathLength = Val.getPathLength();
1783 auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
1784 for (unsigned I = 0; I != PathLength; ++I) {
1785 NewPath[I] = Val.getPathEntry(Index: I);
1786 }
1787 Val.takePath(NewPath);
1788 } else if constexpr (std::is_same_v<T, Pointer>) {
1789 auto &Val = P.deref<Pointer>();
1790 if (Val.isOpaquePointer() && Val.asOpaquePointer().PathLength != 0) {
1791 const OpaquePointer &OP = Val.asOpaquePointer();
1792 auto *NewPath = new (S.P) PointerPathEntry[OP.PathLength];
1793 std::memcpy(dest: NewPath, src: OP.Path, n: OP.PathLength * sizeof(PointerPathEntry));
1794 Val = Pointer(OP.withPath(Path: NewPath, PathLength: OP.PathLength,
1795 FieldTy: OP.getFieldType().getTypePtr(),
1796 PastEnd: OP.isOnePastEnd()),
1797 Val.getByteOffset());
1798 }
1799
1800 } else if constexpr (needsAlloc<T>()) {
1801 auto &Val = P.deref<T>();
1802 if (!Val.singleWord()) {
1803 uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
1804 Val.take(NewMemory);
1805 }
1806 }
1807
1808 P.initialize();
1809 return true;
1810}
1811
1812/// 1) Converts the value on top of the stack to an APValue
1813/// 2) Sets that APValue on \Temp
1814/// 3) Initializes global with index \I with that
1815template <PrimType Name, class T = typename PrimConv<Name>::T>
1816bool InitGlobalTemp(InterpState &S, uint32_t I,
1817 const LifetimeExtendedTemporaryDecl *Temp) {
1818 if (S.EvalMode == EvaluationMode::ConstantFold)
1819 return false;
1820 assert(Temp);
1821
1822 const Pointer &Ptr = S.P.getGlobal(Idx: I);
1823 assert(Ptr.getRootExpr());
1824 S.SeenGlobalTemporaries.push_back(Elt: std::make_pair(x: Ptr.getRootExpr(), y&: Temp));
1825
1826 Ptr.deref<T>() = S.Stk.pop<T>();
1827 Ptr.initialize();
1828 return true;
1829}
1830
1831/// 1) Converts the value on top of the stack to an APValue
1832/// 2) Sets that APValue on \Temp
1833/// 3) Initialized global with index \I with that
1834inline bool InitGlobalTempComp(InterpState &S,
1835 const LifetimeExtendedTemporaryDecl *Temp) {
1836 if (S.EvalMode == EvaluationMode::ConstantFold)
1837 return false;
1838 assert(Temp);
1839
1840 const Pointer &Ptr = S.Stk.peek<Pointer>();
1841 S.SeenGlobalTemporaries.push_back(Elt: std::make_pair(x: Ptr.getRootExpr(), y&: Temp));
1842 return true;
1843}
1844
1845template <PrimType Name, class T = typename PrimConv<Name>::T>
1846bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
1847 if (S.checkingPotentialConstantExpression() && S.Current->isBottomFrame())
1848 return false;
1849 if (!CheckThis(S, OpPC))
1850 return false;
1851 const Pointer &This = S.Current->getThis();
1852 if (!This.isDereferencable())
1853 return false;
1854
1855 const Pointer &Field = This.atField(Off: I);
1856 assert(Field.canBeInitialized());
1857 Field.deref<T>() = S.Stk.pop<T>();
1858 Field.initialize();
1859 return true;
1860}
1861
1862template <PrimType Name, class T = typename PrimConv<Name>::T>
1863bool InitThisFieldActivate(InterpState &S, CodePtr OpPC, uint32_t I) {
1864 if (S.checkingPotentialConstantExpression() && S.Current->isBottomFrame())
1865 return false;
1866 if (!CheckThis(S, OpPC))
1867 return false;
1868 const Pointer &This = S.Current->getThis();
1869 if (!This.isDereferencable())
1870 return false;
1871
1872 const Pointer &Field = This.atField(Off: I);
1873 assert(Field.canBeInitialized());
1874 Field.deref<T>() = S.Stk.pop<T>();
1875 Field.activate();
1876 Field.initialize();
1877 return true;
1878}
1879
1880template <PrimType Name, class T = typename PrimConv<Name>::T>
1881bool InitThisBitField(InterpState &S, CodePtr OpPC, uint32_t FieldOffset,
1882 uint32_t FieldBitWidth) {
1883 if (S.checkingPotentialConstantExpression() && S.Current->isBottomFrame())
1884 return false;
1885 if (!CheckThis(S, OpPC))
1886 return false;
1887 const Pointer &This = S.Current->getThis();
1888 if (!This.isDereferencable())
1889 return false;
1890
1891 const Pointer &Field = This.atField(Off: FieldOffset);
1892 assert(Field.canBeInitialized());
1893 const auto &Value = S.Stk.pop<T>();
1894
1895 if constexpr (isIntegralOrPointer<T>()) {
1896 if (!Value.isNumber())
1897 return false;
1898 }
1899
1900 Field.deref<T>() = Value.truncate(FieldBitWidth);
1901 Field.initialize();
1902 return true;
1903}
1904
1905template <PrimType Name, class T = typename PrimConv<Name>::T>
1906bool InitThisBitFieldActivate(InterpState &S, CodePtr OpPC,
1907 uint32_t FieldOffset, uint32_t FieldBitWidth) {
1908 if (S.checkingPotentialConstantExpression() && S.Current->isBottomFrame())
1909 return false;
1910 if (!CheckThis(S, OpPC))
1911 return false;
1912 const Pointer &This = S.Current->getThis();
1913 if (!This.isDereferencable())
1914 return false;
1915
1916 const Pointer &Field = This.atField(Off: FieldOffset);
1917 assert(Field.canBeInitialized());
1918 const auto &Value = S.Stk.pop<T>();
1919
1920 if constexpr (isIntegralOrPointer<T>()) {
1921 if (!Value.isNumber())
1922 return false;
1923 }
1924
1925 Field.deref<T>() = Value.truncate(FieldBitWidth);
1926 Field.initialize();
1927 Field.activate();
1928 return true;
1929}
1930
1931/// 1) Pops the value from the stack
1932/// 2) Peeks a pointer from the stack
1933/// 3) Pushes the value to field I of the pointer on the stack
1934template <PrimType Name, class T = typename PrimConv<Name>::T>
1935bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
1936 const T &Value = S.Stk.pop<T>();
1937 const Pointer &Ptr = S.Stk.peek<Pointer>();
1938 if (!Ptr.isDereferencable())
1939 return false;
1940
1941 if (!CheckRange(S, OpPC, Ptr, CSK: CSK_Field))
1942 return false;
1943 if (!CheckArray(S, OpPC, Ptr))
1944 return false;
1945
1946 const Pointer &Field = Ptr.atField(Off: I);
1947 Field.deref<T>() = Value;
1948 Field.initialize();
1949 return true;
1950}
1951
1952template <PrimType Name, class T = typename PrimConv<Name>::T>
1953bool InitFieldActivate(InterpState &S, CodePtr OpPC, uint32_t I) {
1954 const T &Value = S.Stk.pop<T>();
1955 const Pointer &Ptr = S.Stk.peek<Pointer>();
1956 if (!Ptr.isDereferencable())
1957 return false;
1958 if (!CheckRange(S, OpPC, Ptr, CSK: CSK_Field))
1959 return false;
1960 if (!CheckArray(S, OpPC, Ptr))
1961 return false;
1962
1963 const Pointer &Field = Ptr.atField(Off: I);
1964 Field.deref<T>() = Value;
1965 Field.activate();
1966 Field.initialize();
1967 return true;
1968}
1969
1970template <PrimType Name, class T = typename PrimConv<Name>::T>
1971bool InitBitField(InterpState &S, CodePtr OpPC, uint32_t FieldOffset,
1972 uint32_t FieldBitWidth) {
1973 const T &Value = S.Stk.pop<T>();
1974 const Pointer &Ptr = S.Stk.peek<Pointer>();
1975 if (!Ptr.isDereferencable())
1976 return false;
1977
1978 if constexpr (isIntegralOrPointer<T>()) {
1979 if (!Value.isNumber())
1980 return false;
1981 }
1982 if (!CheckRange(S, OpPC, Ptr, CSK: CSK_Field))
1983 return false;
1984 if (!CheckArray(S, OpPC, Ptr))
1985 return false;
1986
1987 const Pointer &Field = Ptr.atField(Off: FieldOffset);
1988
1989 unsigned BitWidth = std::min(FieldBitWidth, Value.bitWidth());
1990 if constexpr (needsAlloc<T>()) {
1991 T Result = S.allocAP<T>(Value.bitWidth());
1992 if constexpr (T::isSigned())
1993 Result.copy(
1994 Value.toAPSInt().trunc(BitWidth).sextOrTrunc(Value.bitWidth()));
1995 else
1996 Result.copy(
1997 Value.toAPSInt().trunc(BitWidth).zextOrTrunc(Value.bitWidth()));
1998
1999 Field.deref<T>() = Result;
2000 } else {
2001 Field.deref<T>() = Value.truncate(FieldBitWidth);
2002 }
2003 Field.initialize();
2004 return true;
2005}
2006
2007template <PrimType Name, class T = typename PrimConv<Name>::T>
2008bool InitBitFieldActivate(InterpState &S, CodePtr OpPC, uint32_t FieldOffset,
2009 uint32_t FieldBitWidth) {
2010 const T &Value = S.Stk.pop<T>();
2011 const Pointer &Ptr = S.Stk.peek<Pointer>();
2012 if (!Ptr.isDereferencable())
2013 return false;
2014
2015 if constexpr (isIntegralOrPointer<T>()) {
2016 if (!Value.isNumber())
2017 return false;
2018 }
2019 if (!CheckRange(S, OpPC, Ptr, CSK: CSK_Field))
2020 return false;
2021 if (!CheckArray(S, OpPC, Ptr))
2022 return false;
2023
2024 const Pointer &Field = Ptr.atField(Off: FieldOffset);
2025
2026 unsigned BitWidth = std::min(FieldBitWidth, Value.bitWidth());
2027 if constexpr (needsAlloc<T>()) {
2028 T Result = S.allocAP<T>(Value.bitWidth());
2029 if constexpr (T::isSigned())
2030 Result.copy(
2031 Value.toAPSInt().trunc(BitWidth).sextOrTrunc(Value.bitWidth()));
2032 else
2033 Result.copy(
2034 Value.toAPSInt().trunc(BitWidth).zextOrTrunc(Value.bitWidth()));
2035
2036 Field.deref<T>() = Result;
2037 } else {
2038 Field.deref<T>() = Value.truncate(FieldBitWidth);
2039 }
2040 Field.activate();
2041 Field.initialize();
2042 return true;
2043}
2044
2045//===----------------------------------------------------------------------===//
2046// GetPtr Local/Param/Global/Field/This
2047//===----------------------------------------------------------------------===//
2048
2049inline bool GetPtrLocal(InterpState &S, uint32_t I) {
2050 S.Stk.push<Pointer>(Args: S.Current->getLocalPointer(Offset: I));
2051 return true;
2052}
2053
2054inline bool GetRefLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
2055 Block *LocalBlock = S.Current->getLocalBlock(Offset: I);
2056 return handleReference(S, OpPC, B: LocalBlock);
2057}
2058
2059inline bool GetRefGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
2060 Block *B = S.P.getGlobal(Idx: I);
2061
2062 // If we're currently evaluating this variable, use that in-flight value.
2063 // It will otherwise be diagnosed as non-initialized reference and we will
2064 // complain about a missing initializer.
2065 if (S.EvaluatingDecl && B->getDescriptor()->asVarDecl() == S.EvaluatingDecl) {
2066 S.Stk.push<Pointer>(Args&: B);
2067 return true;
2068 }
2069
2070 if (isConstexprUnknown(B)) {
2071 S.Stk.push<Pointer>(Args&: B);
2072 return true;
2073 }
2074
2075 const auto &Desc = B->getBlockDesc<GlobalInlineDescriptor>();
2076 if (Desc.InitState != GlobalInitState::Initialized)
2077 return diagnoseUninitialized(S, OpPC, Extern: B->isExtern(), B);
2078
2079 S.Stk.push<Pointer>(Args&: B->deref<Pointer>());
2080 return true;
2081}
2082
2083inline bool CheckRefInit(InterpState &S, CodePtr OpPC) {
2084 const Pointer &Ptr = S.Stk.peek<Pointer>();
2085 return CheckRange(S, OpPC, Ptr, AK: AK_Read);
2086}
2087
2088inline bool GetPtrParam(InterpState &S, uint32_t Index) {
2089 if (S.Current->isBottomFrame())
2090 return false;
2091 S.Stk.push<Pointer>(Args: S.Current->getParamPointer(Offset: Index));
2092 return true;
2093}
2094
2095inline bool GetPtrGlobal(InterpState &S, uint32_t I) {
2096 S.Stk.push<Pointer>(Args: S.P.getPtrGlobal(Idx: I));
2097 return true;
2098}
2099
2100/// 1) Peeks a Pointer
2101/// 2) Pushes Pointer.atField(Off) on the stack
2102bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off);
2103bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off);
2104
2105bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off);
2106bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK);
2107
2108bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK,
2109 const Type *TargetType);
2110
2111inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
2112 if (S.checkingPotentialConstantExpression() && S.Current->isBottomFrame())
2113 return false;
2114 if (!CheckThis(S, OpPC))
2115 return false;
2116 const Pointer &This = S.Current->getThis();
2117 if (!This.isBlockPointer())
2118 return false;
2119 S.Stk.push<Pointer>(Args: This.atField(Off));
2120 return true;
2121}
2122
2123inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
2124 if (S.checkingPotentialConstantExpression() && S.Current->isBottomFrame())
2125 return false;
2126 if (!CheckThis(S, OpPC))
2127 return false;
2128 const Pointer &This = S.Current->getThis();
2129 S.Stk.push<Pointer>(Args: This.atField(Off));
2130 return true;
2131}
2132
2133inline bool FinishInitPop(InterpState &S) {
2134 const Pointer &Ptr = S.Stk.pop<Pointer>();
2135 if (Ptr.canBeInitialized())
2136 Ptr.initialize();
2137 return true;
2138}
2139
2140inline bool FinishInit(InterpState &S) {
2141 const Pointer &Ptr = S.Stk.peek<Pointer>();
2142 if (Ptr.canBeInitialized())
2143 Ptr.initialize();
2144 return true;
2145}
2146
2147inline bool FinishInitActivate(InterpState &S) {
2148 const Pointer &Ptr = S.Stk.peek<Pointer>();
2149 if (Ptr.canBeInitialized()) {
2150 Ptr.initialize();
2151 Ptr.activate();
2152 }
2153 return true;
2154}
2155
2156inline bool FinishInitActivatePop(InterpState &S) {
2157 const Pointer &Ptr = S.Stk.pop<Pointer>();
2158 if (Ptr.canBeInitialized()) {
2159 Ptr.initialize();
2160 Ptr.activate();
2161 }
2162 return true;
2163}
2164
2165bool FinishInitGlobal(InterpState &S);
2166
2167inline bool Dump(InterpState &S) {
2168 S.Stk.dump();
2169 return true;
2170}
2171
2172inline bool CheckNull(InterpState &S, CodePtr OpPC) {
2173 const auto &Ptr = S.Stk.peek<Pointer>();
2174 if (Ptr.isZero()) {
2175 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
2176 DiagId: diag::note_constexpr_dereferencing_null);
2177 return S.noteUndefinedBehavior();
2178 }
2179 return true;
2180}
2181
2182bool virtBaseHelper(InterpState &S, const CXXRecordDecl *Decl,
2183 const Pointer &Ptr);
2184
2185inline bool GetPtrVirtBasePop(InterpState &S, CodePtr OpPC,
2186 const CXXRecordDecl *D) {
2187 assert(D);
2188 const Pointer &Ptr = S.Stk.pop<Pointer>();
2189 if (!CheckNull(S, OpPC, Ptr, CSK: CSK_Base))
2190 return false;
2191 return virtBaseHelper(S, Decl: D, Ptr);
2192}
2193
2194inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC,
2195 const CXXRecordDecl *D) {
2196 assert(D);
2197 const Pointer &Ptr = S.Stk.peek<Pointer>();
2198 if (!CheckNull(S, OpPC, Ptr, CSK: CSK_Base))
2199 return false;
2200 return virtBaseHelper(S, Decl: D, Ptr);
2201}
2202
2203inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC,
2204 const CXXRecordDecl *D) {
2205 assert(D);
2206 if (S.checkingPotentialConstantExpression())
2207 return false;
2208 if (!CheckThis(S, OpPC))
2209 return false;
2210 const Pointer &This = S.Current->getThis();
2211 return virtBaseHelper(S, Decl: D, Ptr: This);
2212}
2213
2214//===----------------------------------------------------------------------===//
2215// Load, Store, Init
2216//===----------------------------------------------------------------------===//
2217
2218template <PrimType Name, class T = typename PrimConv<Name>::T>
2219bool Load(InterpState &S, CodePtr OpPC) {
2220 const Pointer &Ptr = S.Stk.peek<Pointer>();
2221 if (!CheckLoad(S, OpPC, Ptr))
2222 return false;
2223 if (!Ptr.canDeref(T: Name))
2224 return false;
2225 S.Stk.push<T>(Ptr.load<T>());
2226 return true;
2227}
2228
2229template <PrimType Name, class T = typename PrimConv<Name>::T>
2230bool LoadPop(InterpState &S, CodePtr OpPC) {
2231 const Pointer &Ptr = S.Stk.pop<Pointer>();
2232 if (!CheckLoad(S, OpPC, Ptr))
2233 return false;
2234 if (!Ptr.canDeref(T: Name))
2235 return false;
2236 S.Stk.push<T>(Ptr.load<T>());
2237 return true;
2238}
2239
2240/// Like LoadPop above, but if any of the checks fail, we
2241/// turn the pointer into an opaque pointer of appropriate type.
2242inline bool LoadPopL(InterpState &S, CodePtr OpPC) {
2243 const Pointer &Ptr = S.Stk.pop<Pointer>();
2244 auto *P = S.getEvalStatus().Diag;
2245 S.getEvalStatus().Diag = nullptr;
2246
2247 bool Failed = false;
2248 if (!CheckLoad(S, OpPC, Ptr))
2249 Failed = true;
2250 if (!Ptr.isBlockPointer())
2251 Failed = true;
2252 if (!Failed && !Ptr.canDeref(T: PT_Ptr))
2253 Failed = true;
2254 S.getEvalStatus().Diag = P;
2255
2256 if (Failed) {
2257 if (Ptr.isOpaquePointer()) {
2258 const OpaquePointer &OP = Ptr.asOpaquePointer();
2259
2260 if (!Ptr.asOpaquePointer().Base.getType()->isPointerType())
2261 return false;
2262
2263 QualType T = Ptr.getType();
2264 S.Stk.push<Pointer>(Args: OP.withFieldType(FieldTy: T.getTypePtr()),
2265 Args: Ptr.getByteOffset());
2266 return true;
2267 }
2268
2269 // Convert the block pointer to an opaque pointer.
2270 if (!Ptr.isBlockPointer())
2271 return false;
2272 // FIXME: I *think* we need more information here than just the base.
2273 S.Stk.push<Pointer>(Args: Ptr.getDeclDesc()->asValueDecl());
2274 } else {
2275 S.Stk.push<Pointer>(Args&: Ptr.deref<Pointer>());
2276 }
2277 return true;
2278}
2279
2280template <PrimType Name, class T = typename PrimConv<Name>::T>
2281bool Store(InterpState &S, CodePtr OpPC) {
2282 const T &Value = S.Stk.pop<T>();
2283 const Pointer &Ptr = S.Stk.peek<Pointer>();
2284 if (!CheckStore(S, OpPC, Ptr))
2285 return false;
2286 if (!Ptr.canDeref(T: Name))
2287 return false;
2288 if (Ptr.canBeInitialized())
2289 Ptr.initialize();
2290 Ptr.deref<T>() = Value;
2291 return true;
2292}
2293
2294template <PrimType Name, class T = typename PrimConv<Name>::T>
2295bool StorePop(InterpState &S, CodePtr OpPC) {
2296 const T &Value = S.Stk.pop<T>();
2297 const Pointer &Ptr = S.Stk.pop<Pointer>();
2298 if (!CheckStore(S, OpPC, Ptr))
2299 return false;
2300 if (!Ptr.canDeref(T: Name))
2301 return false;
2302 if (Ptr.canBeInitialized())
2303 Ptr.initialize();
2304 Ptr.deref<T>() = Value;
2305 return true;
2306}
2307
2308static inline bool Activate(InterpState &S) {
2309 const Pointer &Ptr = S.Stk.peek<Pointer>();
2310 if (Ptr.canBeInitialized())
2311 Ptr.activate();
2312 return true;
2313}
2314
2315static inline bool ActivateThisField(InterpState &S, uint32_t I) {
2316 if (S.checkingPotentialConstantExpression())
2317 return false;
2318 if (!S.Current->hasThisPointer())
2319 return false;
2320
2321 const Pointer &Ptr = S.Current->getThis();
2322 assert(Ptr.atField(I).canBeInitialized());
2323 Ptr.atField(Off: I).activate();
2324 return true;
2325}
2326
2327template <PrimType Name, class T = typename PrimConv<Name>::T>
2328bool StoreActivate(InterpState &S, CodePtr OpPC) {
2329 const T &Value = S.Stk.pop<T>();
2330 const Pointer &Ptr = S.Stk.peek<Pointer>();
2331
2332 if (!CheckStore(S, OpPC, Ptr, AK: AK_Assign, /*WillBeActivated=*/WillBeActivated: true))
2333 return false;
2334 if (Ptr.canBeInitialized()) {
2335 Ptr.initialize();
2336 Ptr.activate();
2337 }
2338 Ptr.deref<T>() = Value;
2339 return true;
2340}
2341
2342template <PrimType Name, class T = typename PrimConv<Name>::T>
2343bool StoreActivatePop(InterpState &S, CodePtr OpPC) {
2344 const T &Value = S.Stk.pop<T>();
2345 const Pointer &Ptr = S.Stk.pop<Pointer>();
2346
2347 if (!CheckStore(S, OpPC, Ptr, AK: AK_Assign, /*WillBeActivated=*/WillBeActivated: true))
2348 return false;
2349 if (Ptr.canBeInitialized()) {
2350 Ptr.initialize();
2351 Ptr.activate();
2352 }
2353 Ptr.deref<T>() = Value;
2354 return true;
2355}
2356
2357template <PrimType Name, class T = typename PrimConv<Name>::T>
2358bool StoreBitField(InterpState &S, CodePtr OpPC) {
2359 const T &Value = S.Stk.pop<T>();
2360 const Pointer &Ptr = S.Stk.peek<Pointer>();
2361
2362 if (!CheckStore(S, OpPC, Ptr))
2363 return false;
2364 if (Ptr.canBeInitialized())
2365 Ptr.initialize();
2366 if (const auto *FD = Ptr.getField())
2367 Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
2368 else
2369 Ptr.deref<T>() = Value;
2370 return true;
2371}
2372
2373template <PrimType Name, class T = typename PrimConv<Name>::T>
2374bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
2375 const T &Value = S.Stk.pop<T>();
2376 const Pointer &Ptr = S.Stk.pop<Pointer>();
2377 if (!CheckStore(S, OpPC, Ptr))
2378 return false;
2379 if (Ptr.canBeInitialized())
2380 Ptr.initialize();
2381 if (const auto *FD = Ptr.getField())
2382 Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
2383 else
2384 Ptr.deref<T>() = Value;
2385 return true;
2386}
2387
2388template <PrimType Name, class T = typename PrimConv<Name>::T>
2389bool StoreBitFieldActivate(InterpState &S, CodePtr OpPC) {
2390 const T &Value = S.Stk.pop<T>();
2391 const Pointer &Ptr = S.Stk.peek<Pointer>();
2392
2393 if (!CheckStore(S, OpPC, Ptr, AK: AK_Assign, /*WillBeActivated=*/WillBeActivated: true))
2394 return false;
2395 if (Ptr.canBeInitialized()) {
2396 Ptr.initialize();
2397 Ptr.activate();
2398 }
2399 if (const auto *FD = Ptr.getField())
2400 Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
2401 else
2402 Ptr.deref<T>() = Value;
2403 return true;
2404}
2405
2406template <PrimType Name, class T = typename PrimConv<Name>::T>
2407bool StoreBitFieldActivatePop(InterpState &S, CodePtr OpPC) {
2408 const T &Value = S.Stk.pop<T>();
2409 const Pointer &Ptr = S.Stk.pop<Pointer>();
2410
2411 if (!CheckStore(S, OpPC, Ptr, AK: AK_Assign, /*WillBeActivated=*/WillBeActivated: true))
2412 return false;
2413 if (Ptr.canBeInitialized()) {
2414 Ptr.initialize();
2415 Ptr.activate();
2416 }
2417 if (const auto *FD = Ptr.getField())
2418 Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
2419 else
2420 Ptr.deref<T>() = Value;
2421 return true;
2422}
2423
2424template <PrimType Name, class T = typename PrimConv<Name>::T>
2425bool Init(InterpState &S, CodePtr OpPC) {
2426 const T &Value = S.Stk.pop<T>();
2427 const Pointer &Ptr = S.Stk.peek<Pointer>();
2428 if (!CheckInit(S, OpPC, Ptr))
2429 return false;
2430 Ptr.initialize();
2431 new (&Ptr.deref<T>()) T(Value);
2432 return true;
2433}
2434
2435template <PrimType Name, class T = typename PrimConv<Name>::T>
2436bool InitPop(InterpState &S, CodePtr OpPC) {
2437 const T &Value = S.Stk.pop<T>();
2438 const Pointer &Ptr = S.Stk.pop<Pointer>();
2439 if (!CheckInit(S, OpPC, Ptr))
2440 return false;
2441 Ptr.initialize();
2442 new (&Ptr.deref<T>()) T(Value);
2443 return true;
2444}
2445
2446/// 1) Pops the value from the stack
2447/// 2) Peeks a pointer and gets its index \Idx
2448/// 3) Sets the value on the pointer, leaving the pointer on the stack.
2449template <PrimType Name, class T = typename PrimConv<Name>::T>
2450bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
2451 const T &Value = S.Stk.pop<T>();
2452 const Pointer &Ptr = S.Stk.peek<Pointer>();
2453
2454 if (Ptr.isConstexprUnknown())
2455 return false;
2456
2457 const Descriptor *Desc = Ptr.getFieldDesc();
2458 if (Desc->isUnknownSizeArray())
2459 return false;
2460
2461 // In the unlikely event that we're initializing the first item of
2462 // a non-array, skip the atIndex().
2463 if (Idx == 0 && !Desc->isArray()) {
2464 Ptr.initialize();
2465 new (&Ptr.deref<T>()) T(Value);
2466 return true;
2467 }
2468
2469 if (!CheckLive(S, OpPC, Ptr, AK: AK_Assign))
2470 return false;
2471 if (Idx >= Desc->getNumElems()) {
2472 // CheckRange.
2473 if (S.getLangOpts().CPlusPlus) {
2474 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
2475 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_access_past_end)
2476 << AK_Assign << S.Current->getRange(PC: OpPC);
2477 }
2478 return false;
2479 }
2480 Ptr.initializeElement(Index: Idx);
2481 new (&Ptr.elem<T>(Idx)) T(Value);
2482 return true;
2483}
2484
2485/// The same as InitElem, but pops the pointer as well.
2486template <PrimType Name, class T = typename PrimConv<Name>::T>
2487bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
2488 const T &Value = S.Stk.pop<T>();
2489 const Pointer &Ptr = S.Stk.pop<Pointer>();
2490
2491 if (Ptr.isConstexprUnknown())
2492 return false;
2493
2494 const Descriptor *Desc = Ptr.getFieldDesc();
2495 if (Desc->isUnknownSizeArray())
2496 return false;
2497
2498 // In the unlikely event that we're initializing the first item of
2499 // a non-array, skip the atIndex().
2500 if (Idx == 0 && !Desc->isArray()) {
2501 Ptr.initialize();
2502 new (&Ptr.deref<T>()) T(Value);
2503 return true;
2504 }
2505
2506 if (!CheckLive(S, OpPC, Ptr, AK: AK_Assign))
2507 return false;
2508 if (Idx >= Desc->getNumElems()) {
2509 // CheckRange.
2510 if (S.getLangOpts().CPlusPlus) {
2511 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
2512 S.FFDiag(SI: Loc, DiagId: diag::note_constexpr_access_past_end)
2513 << AK_Assign << S.Current->getRange(PC: OpPC);
2514 }
2515 return false;
2516 }
2517 Ptr.initializeElement(Index: Idx);
2518 new (&Ptr.elem<T>(Idx)) T(Value);
2519 return true;
2520}
2521
2522inline bool ToMemberPtr(InterpState &S) {
2523 const auto &Member = S.Stk.pop<MemberPointer>();
2524 const auto &Base = S.Stk.pop<Pointer>();
2525
2526 S.Stk.push<MemberPointer>(Args: Member.takeInstance(Instance: Base));
2527 return true;
2528}
2529
2530inline bool CastMemberPtrPtr(InterpState &S, CodePtr OpPC) {
2531 const auto &MP = S.Stk.pop<MemberPointer>();
2532
2533 if (std::optional<Pointer> Ptr = MP.toPointer(Ctx: S.Ctx)) {
2534 S.Stk.push<Pointer>(Args&: *Ptr);
2535 return true;
2536 }
2537 return Invalid(S, OpPC);
2538}
2539
2540bool Memcpy(InterpState &S, CodePtr OpPC);
2541bool TrivialCopy(InterpState &S, CodePtr OpPC, bool Activate,
2542 const Function *Func);
2543
2544//===----------------------------------------------------------------------===//
2545// AddOffset, SubOffset
2546//===----------------------------------------------------------------------===//
2547
2548template <class T, ArithOp Op>
2549std::optional<Pointer> OffsetHelper(InterpState &S, CodePtr OpPC,
2550 const T &Offset, const Pointer &Ptr,
2551 bool IsPointerArith = false) {
2552 // A zero offset does not change the pointer.
2553 if (Offset.isZero())
2554 return Ptr;
2555
2556 if (IsPointerArith && !CheckNull(S, OpPC, Ptr, CSK: CSK_ArrayIndex)) {
2557 // The CheckNull will have emitted a note already, but we only
2558 // abort in C++, since this is fine in C.
2559 if (S.getLangOpts().CPlusPlus)
2560 return std::nullopt;
2561 }
2562
2563 // Arrays of unknown bounds cannot have pointers into them.
2564 if (!CheckArray(S, OpPC, Ptr))
2565 return std::nullopt;
2566
2567 // This is much simpler for integral pointers, so handle them first.
2568 if (Ptr.isIntegralPointer()) {
2569 uint64_t V = Ptr.getIntegerRepresentation();
2570 QualType ElemType = Ptr.asIntPointer().getPointeeType();
2571 uint64_t ElemSize =
2572 (ElemType.isNull() || ElemType->isVoidType())
2573 ? 1u
2574 : S.getASTContext().getTypeSizeInChars(T: ElemType).getQuantity();
2575 uint64_t O = static_cast<uint64_t>(Offset) * ElemSize;
2576 if constexpr (Op == ArithOp::Add) {
2577 return Pointer(V + O, Ptr.asIntPointer().getType());
2578 } else
2579 return Pointer(V - O, Ptr.asIntPointer().getType());
2580 } else if (Ptr.isFunctionPointer()) {
2581 uint64_t O = static_cast<uint64_t>(Offset);
2582 uint64_t N;
2583 if constexpr (Op == ArithOp::Add)
2584 N = Ptr.getByteOffset() + O;
2585 else
2586 N = Ptr.getByteOffset() - O;
2587
2588 if (N > 1)
2589 diagnoseArrayIndex(S, OpPC, Index: APSInt::getUnsigned(X: N), NumElems: 0, /*IsArray=*/IsArray: false);
2590 return Pointer(Ptr.asFunctionPointer().Func, N);
2591 } else if (Ptr.isStringPointer()) {
2592 int64_t NewOffset;
2593 if constexpr (Op == ArithOp::Add)
2594 NewOffset = Ptr.getByteOffset() + static_cast<int64_t>(Offset);
2595 else
2596 NewOffset = Ptr.getByteOffset() - static_cast<int64_t>(Offset);
2597 if (NewOffset < 0 ||
2598 NewOffset > (Ptr.asStringPointer().getLiteral()->getLength() + 1)) {
2599 diagnoseArrayIndex(S, OpPC, Index: APSInt::get(X: NewOffset),
2600 NumElems: (Ptr.asStringPointer().getLiteral()->getLength() + 1));
2601 return std::nullopt;
2602 }
2603 return Pointer(Ptr.asStringPointer(), NewOffset);
2604 } else if (!Ptr.isBlockPointer()) {
2605 return std::nullopt;
2606 }
2607
2608 assert(Ptr.isBlockPointer());
2609
2610 uint64_t MaxIndex = static_cast<uint64_t>(Ptr.getNumElems());
2611 uint64_t Index;
2612 if (Ptr.isOnePastEnd())
2613 Index = MaxIndex;
2614 else
2615 Index = Ptr.getIndex();
2616
2617 bool Invalid = false;
2618 // Helper to report an invalid offset, computed as APSInt.
2619 auto DiagInvalidOffset = [&]() -> void {
2620 const unsigned Bits = Offset.bitWidth();
2621 APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), /*IsUnsigend=*/false);
2622 APSInt APIndex(APInt(Bits + 2, Index, /*IsSigned=*/true),
2623 /*IsUnsigned=*/false);
2624 APSInt NewIndex =
2625 (Op == ArithOp::Add) ? (APIndex + APOffset) : (APIndex - APOffset);
2626 diagnoseArrayIndex(S, OpPC, Index: NewIndex, NumElems: MaxIndex, IsArray: Ptr.inArray());
2627 Invalid = true;
2628 };
2629
2630 uint64_t IOffset = static_cast<uint64_t>(Offset);
2631 uint64_t MaxOffset = MaxIndex - Index;
2632
2633 if constexpr (Op == ArithOp::Add) {
2634 // If the new offset would be negative, bail out.
2635 if (Offset.isNegative() && (Offset.isMin() || -IOffset > Index))
2636 DiagInvalidOffset();
2637
2638 // If the new offset would be out of bounds, bail out.
2639 if (Offset.isPositive() && IOffset > MaxOffset)
2640 DiagInvalidOffset();
2641 } else {
2642 // If the new offset would be negative, bail out.
2643 if (Offset.isPositive() && Index < IOffset)
2644 DiagInvalidOffset();
2645
2646 // If the new offset would be out of bounds, bail out.
2647 if (Offset.isNegative() && (Offset.isMin() || -IOffset > MaxOffset))
2648 DiagInvalidOffset();
2649 }
2650
2651 if (Invalid && (S.getLangOpts().CPlusPlus || Ptr.inArray()))
2652 return std::nullopt;
2653
2654 // Offset is valid - compute it on unsigned.
2655 int64_t WideIndex = static_cast<int64_t>(Index);
2656 int64_t WideOffset = static_cast<int64_t>(Offset);
2657 int64_t Result;
2658 if constexpr (Op == ArithOp::Add)
2659 Result = WideIndex + WideOffset;
2660 else
2661 Result = WideIndex - WideOffset;
2662
2663 // When the pointer is one-past-end, going back to index 0 is the only
2664 // useful thing we can do. Any other index has been diagnosed before and
2665 // we don't get here.
2666 if (Result == 0 && Ptr.isOnePastEnd()) {
2667 if (Ptr.getFieldDesc()->isArray())
2668 return Ptr.atIndex(Idx: 0);
2669 return Pointer(Ptr.asBlockPointer().Pointee, Ptr.asBlockPointer().Base);
2670 }
2671
2672 return Ptr.atIndex(Idx: static_cast<uint64_t>(Result));
2673}
2674
2675std::optional<Pointer> addSubOffsetOpaque(InterpState &S, CodePtr OpPC,
2676 const Pointer &Ptr, APSInt &&Offset,
2677 ArithOp Op);
2678template <PrimType Name, class T = typename PrimConv<Name>::T>
2679bool AddOffset(InterpState &S, CodePtr OpPC) {
2680 const T &Offset = S.Stk.pop<T>();
2681 const Pointer &Ptr = S.Stk.pop<Pointer>().expand();
2682
2683 if (Ptr.isOpaquePointer()) {
2684 if (std::optional<Pointer> Result =
2685 addSubOffsetOpaque(S, OpPC, Ptr, Offset.toAPSInt(), ArithOp::Add)) {
2686 S.Stk.push<Pointer>(Args&: *Result);
2687 return true;
2688 }
2689 return false;
2690 }
2691
2692 if (std::optional<Pointer> Result = OffsetHelper<T, ArithOp::Add>(
2693 S, OpPC, Offset, Ptr, /*IsPointerArith=*/true)) {
2694 S.Stk.push<Pointer>(Args: Result->narrow());
2695 return true;
2696 }
2697 return false;
2698}
2699
2700template <PrimType Name, class T = typename PrimConv<Name>::T>
2701bool SubOffset(InterpState &S, CodePtr OpPC) {
2702 const T &Offset = S.Stk.pop<T>();
2703 const Pointer &Ptr = S.Stk.pop<Pointer>().expand();
2704
2705 if (Ptr.isOpaquePointer()) {
2706 if (std::optional<Pointer> Result =
2707 addSubOffsetOpaque(S, OpPC, Ptr, Offset.toAPSInt(), ArithOp::Sub)) {
2708 S.Stk.push<Pointer>(Args&: *Result);
2709 return true;
2710 }
2711 return false;
2712 }
2713
2714 if (std::optional<Pointer> Result = OffsetHelper<T, ArithOp::Sub>(
2715 S, OpPC, Offset, Ptr, /*IsPointerArith=*/true)) {
2716 S.Stk.push<Pointer>(Args: Result->narrow());
2717 return true;
2718 }
2719 return false;
2720}
2721
2722inline bool GetOpaquePtr(InterpState &S, DeclOrExpr DOE,
2723 bool ConstexprUnknown) {
2724 S.Stk.push<Pointer>(Args&: DOE, Args&: ConstexprUnknown);
2725 return true;
2726}
2727
2728template <ArithOp Op>
2729static inline bool IncDecPtrHelper(InterpState &S, CodePtr OpPC,
2730 const Pointer &Ptr) {
2731 if (!Ptr.isDereferencable())
2732 return false;
2733
2734 using OneT = Char<false>;
2735
2736 const Pointer &P = Ptr.deref<Pointer>();
2737 if (!CheckNull(S, OpPC, Ptr: P, CSK: CSK_ArrayIndex))
2738 return false;
2739
2740 // Get the current value on the stack.
2741 S.Stk.push<Pointer>(Args: P);
2742
2743 if (P.isOpaquePointer()) {
2744 if (std::optional<Pointer> Result =
2745 addSubOffsetOpaque(S, OpPC, Ptr: P, Offset: APSInt(APInt(1, 1), true), Op)) {
2746 Ptr.deref<Pointer>() = *Result;
2747 return true;
2748 }
2749 return false;
2750 }
2751
2752 // Now the current Ptr again and a constant 1.
2753 OneT One = OneT::from(t: 1);
2754 if (std::optional<Pointer> Result =
2755 OffsetHelper<OneT, Op>(S, OpPC, One, P, /*IsPointerArith=*/true)) {
2756 // Store the new value.
2757 Ptr.deref<Pointer>() = Result->narrow();
2758 return true;
2759 }
2760 return false;
2761}
2762
2763static inline bool IncPtr(InterpState &S, CodePtr OpPC) {
2764 const Pointer &Ptr = S.Stk.pop<Pointer>();
2765
2766 if (!Ptr.isInitialized())
2767 return diagnoseUninitialized(S, OpPC, Ptr, AK: AK_Increment);
2768
2769 return IncDecPtrHelper<ArithOp::Add>(S, OpPC, Ptr);
2770}
2771
2772static inline bool DecPtr(InterpState &S, CodePtr OpPC) {
2773 const Pointer &Ptr = S.Stk.pop<Pointer>();
2774
2775 if (!Ptr.isInitialized())
2776 return diagnoseUninitialized(S, OpPC, Ptr, AK: AK_Decrement);
2777
2778 return IncDecPtrHelper<ArithOp::Sub>(S, OpPC, Ptr);
2779}
2780
2781/// 1) Pops a Pointer from the stack.
2782/// 2) Pops another Pointer from the stack.
2783/// 3) Pushes the difference of the indices of the two pointers on the stack.
2784template <PrimType Name, class T = typename PrimConv<Name>::T>
2785inline bool SubPtr(InterpState &S, CodePtr OpPC, uint32_t ElemSize) {
2786 const Pointer &LHS = S.Stk.pop<Pointer>().expand();
2787 const Pointer &RHS = S.Stk.pop<Pointer>().expand();
2788
2789 if (LHS.pointsToLabel() || RHS.pointsToLabel()) {
2790 if constexpr (isIntegralOrPointer<T>()) {
2791 const AddrLabelExpr *LHSAddrExpr = LHS.getPointedToLabel();
2792 const AddrLabelExpr *RHSAddrExpr = RHS.getPointedToLabel();
2793 if (!LHSAddrExpr || !RHSAddrExpr) {
2794 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
2795 DiagId: diag::note_constexpr_pointer_arith_unspecified)
2796 << LHS.toDiagnosticString(Ctx: S.getASTContext())
2797 << RHS.toDiagnosticString(Ctx: S.getASTContext());
2798 return false;
2799 }
2800
2801 if (LHSAddrExpr->getLabel()->getDeclContext() !=
2802 RHSAddrExpr->getLabel()->getDeclContext())
2803 return Invalid(S, OpPC);
2804
2805 S.Stk.push<T>(LHSAddrExpr, RHSAddrExpr);
2806 return true;
2807 }
2808 // Can't represent an address-label-diff in these types.
2809 return false;
2810 }
2811
2812 if (!Pointer::hasSameBase(A: LHS, B: RHS)) {
2813 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
2814 DiagId: diag::note_constexpr_pointer_arith_unspecified)
2815 << LHS.toDiagnosticString(Ctx: S.getASTContext())
2816 << RHS.toDiagnosticString(Ctx: S.getASTContext());
2817 return false;
2818 }
2819
2820 if (ElemSize == 0) {
2821 QualType PtrT = S.getASTContext().getBaseElementType(QT: LHS.getType());
2822 QualType ArrayTy = S.getASTContext().getConstantArrayType(
2823 EltTy: PtrT, ArySize: APInt::getZero(numBits: 1), SizeExpr: nullptr, ASM: ArraySizeModifier::Normal, IndexTypeQuals: 0);
2824 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
2825 DiagId: diag::note_constexpr_pointer_subtraction_zero_size)
2826 << ArrayTy;
2827
2828 return false;
2829 }
2830
2831 if (LHS == RHS) {
2832 S.Stk.push<T>();
2833 return true;
2834 }
2835
2836 // C++11 [expr.add]p6:
2837 // Unless both pointers point to elements of the same array object, or
2838 // one past the last element of the array object, the behavior is
2839 // undefined.
2840 if (LHS.isBlockPointer() && !Pointer::elemsOfSameArray(A: LHS, B: RHS))
2841 S.CCEDiag(SI: S.Current->getSource(PC: OpPC),
2842 DiagId: diag::note_constexpr_pointer_subtraction_not_same_array);
2843
2844 std::optional<size_t> VL = LHS.computeLayoutOffset(ASTCtx: S.getASTContext());
2845 if (!VL)
2846 return false;
2847 std::optional<size_t> VR = RHS.computeLayoutOffset(ASTCtx: S.getASTContext());
2848 if (!VR)
2849 return false;
2850
2851 // We allow (VL - VR) / Elemsize to have non-zero remainder. This happens for
2852 // invalid expressions where LHS and RHS are of different types.
2853 int64_t R64 =
2854 (static_cast<int64_t>(*VL) - static_cast<int64_t>(*VR)) / ElemSize;
2855 if (static_cast<int64_t>(T::from(R64)) != R64)
2856 return handleOverflow(S, OpPC, SrcValue: R64);
2857
2858 S.Stk.push<T>(T::from(R64));
2859 return true;
2860}
2861
2862inline bool InitScope(InterpState &S, uint32_t I) {
2863 S.Current->initScope(Idx: I);
2864 return true;
2865}
2866
2867inline bool EnableLocal(InterpState &S, uint32_t I) {
2868 assert(!S.Current->isLocalEnabled(I));
2869 S.Current->enableLocal(Idx: I);
2870 return true;
2871}
2872
2873inline bool GetLocalEnabled(InterpState &S, uint32_t I) {
2874 assert(S.Current);
2875 S.Stk.push<bool>(Args: S.Current->isLocalEnabled(Idx: I));
2876 return true;
2877}
2878
2879//===----------------------------------------------------------------------===//
2880// Cast, CastFP
2881//===----------------------------------------------------------------------===//
2882
2883template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) {
2884 using T = typename PrimConv<TIn>::T;
2885 using U = typename PrimConv<TOut>::T;
2886
2887 auto In = S.Stk.pop<T>();
2888
2889 if constexpr (isIntegralOrPointer<T>()) {
2890 if (In.getKind() != IntegralKind::Number &&
2891 In.getKind() != IntegralKind::AddrLabelDiff) {
2892 if (!CheckIntegralAddressCast(S, OpPC, U::bitWidth()))
2893 return Invalid(S, OpPC);
2894 } else if (In.getKind() == IntegralKind::AddrLabelDiff) {
2895 // Allow casts of address-of-label differences if they are no-ops
2896 // or narrowing, if the result is at least 32 bits wide.
2897 // (The narrowing case isn't actually guaranteed to
2898 // be constant-evaluatable except in some narrow cases which are hard
2899 // to detect here. We let it through on the assumption the user knows
2900 // what they are doing.)
2901 if (!(U::bitWidth() >= 32 && U::bitWidth() <= In.bitWidth()))
2902 return false;
2903 }
2904 }
2905
2906 S.Stk.push<U>(U::from(In));
2907 return true;
2908}
2909
2910/// 1) Pops a Floating from the stack.
2911/// 2) Pushes a new floating on the stack that uses the given semantics.
2912inline bool CastFP(InterpState &S, const llvm::fltSemantics *Sem,
2913 llvm::RoundingMode RM) {
2914 Floating F = S.Stk.pop<Floating>();
2915 Floating Result = S.allocFloat(Sem: *Sem);
2916 F.toSemantics(Sem, RM, Result: &Result);
2917 S.Stk.push<Floating>(Args&: Result);
2918 return true;
2919}
2920
2921inline bool CastFixedPoint(InterpState &S, CodePtr OpPC, uint32_t FPS) {
2922 FixedPointSemantics TargetSemantics =
2923 FixedPointSemantics::getFromOpaqueInt(FPS);
2924 const auto &Source = S.Stk.pop<FixedPoint>();
2925
2926 bool Overflow;
2927 FixedPoint Result = Source.toSemantics(Sem: TargetSemantics, Overflow: &Overflow);
2928
2929 if (Overflow && !handleFixedPointOverflow(S, OpPC, FP: Result))
2930 return false;
2931
2932 S.Stk.push<FixedPoint>(Args&: Result);
2933 return true;
2934}
2935
2936/// Like Cast(), but we cast to an arbitrary-bitwidth integral, so we need
2937/// to know what bitwidth the result should be.
2938template <PrimType Name, class T = typename PrimConv<Name>::T>
2939bool CastAP(InterpState &S, uint32_t BitWidth) {
2940 T Source = S.Stk.pop<T>();
2941
2942 if constexpr (isIntegralOrPointer<T>()) {
2943 if (!Source.isNumber())
2944 return false;
2945 }
2946
2947 auto Result = S.allocAP<IntegralAP<false>>(BitWidth);
2948 // Copy data.
2949 {
2950 APInt SourceInt = Source.toAPSInt().extOrTrunc(BitWidth);
2951 Result.copy(V: SourceInt);
2952 }
2953 S.Stk.push<IntegralAP<false>>(Args&: Result);
2954 return true;
2955}
2956
2957template <PrimType Name, class T = typename PrimConv<Name>::T>
2958bool CastAPS(InterpState &S, uint32_t BitWidth) {
2959 T Source = S.Stk.pop<T>();
2960
2961 if constexpr (isIntegralOrPointer<T>()) {
2962 if (!Source.isNumber())
2963 return false;
2964 }
2965
2966 auto Result = S.allocAP<IntegralAP<true>>(BitWidth);
2967 // Copy data.
2968 {
2969 APInt SourceInt = Source.toAPSInt().extOrTrunc(BitWidth);
2970 Result.copy(V: SourceInt);
2971 }
2972 S.Stk.push<IntegralAP<true>>(Args&: Result);
2973 return true;
2974}
2975
2976// Cast an AP integer to Sint64 for use as an offsetof array index, failing
2977// constant evaluation if the value is negative or too large to fit in Sint64
2978// (i.e. truncation would change the value).
2979template <PrimType Name, class T = typename PrimConv<Name>::T>
2980bool CastAPToOffsetIndex(InterpState &S, CodePtr OpPC) {
2981 T Source = S.Stk.pop<T>();
2982 APSInt Val = Source.toAPSInt();
2983 if (Val.isNegative() || Val.getActiveBits() > 63)
2984 return Invalid(S, OpPC);
2985 S.Stk.push<Integral<64, true>>(
2986 Args: Integral<64, true>::from(V: (int64_t)Val.getZExtValue()));
2987 return true;
2988}
2989
2990template <PrimType Name, class T = typename PrimConv<Name>::T>
2991bool CastIntegralFloating(InterpState &S, CodePtr OpPC,
2992 const llvm::fltSemantics *Sem, uint32_t FPOI) {
2993 const T &From = S.Stk.pop<T>();
2994
2995 if constexpr (isIntegralOrPointer<T>()) {
2996 if (!From.isNumber())
2997 return false;
2998 }
2999
3000 APSInt FromAP = From.toAPSInt();
3001
3002 FPOptions FPO = FPOptions::getFromOpaqueInt(Value: FPOI);
3003 Floating Result = S.allocFloat(Sem: *Sem);
3004 auto Status =
3005 Floating::fromIntegral(Val: FromAP, Sem: *Sem, RM: getRoundingMode(FPO), Result: &Result);
3006 S.Stk.push<Floating>(Args&: Result);
3007
3008 return CheckFloatResult(S, OpPC, Result, Status, FPO);
3009}
3010
3011template <PrimType Name, class T = typename PrimConv<Name>::T>
3012bool CastFloatingIntegral(InterpState &S, CodePtr OpPC, uint32_t FPOI) {
3013 const Floating &F = S.Stk.pop<Floating>();
3014
3015 if constexpr (std::is_same_v<T, Boolean>) {
3016 S.Stk.push<T>(T(F.isNonZero()));
3017 return true;
3018 } else {
3019 APSInt Result(std::max(8u, T::bitWidth()),
3020 /*IsUnsigned=*/!T::isSigned());
3021 auto Status = F.convertToInteger(Result);
3022
3023 // Float-to-Integral overflow check.
3024 if ((Status & APFloat::opStatus::opInvalidOp)) {
3025 const Expr *E = S.Current->getExpr(PC: OpPC);
3026 QualType Type = E->getType();
3027
3028 S.CCEDiag(E, DiagId: diag::note_constexpr_overflow) << F.getAPFloat() << Type;
3029 if (S.noteUndefinedBehavior()) {
3030 S.Stk.push<T>(T(Result));
3031 return true;
3032 }
3033 return false;
3034 }
3035
3036 FPOptions FPO = FPOptions::getFromOpaqueInt(Value: FPOI);
3037 S.Stk.push<T>(T(Result));
3038 return CheckFloatResult(S, OpPC, Result: F, Status, FPO);
3039 }
3040}
3041
3042inline bool AddrOf(InterpState &S, CodePtr OpPC) {
3043 const Pointer Ptr = S.Stk.pop<Pointer>();
3044
3045 if (Ptr.isOpaquePointer()) {
3046 const OpaquePointer &OP = Ptr.asOpaquePointer();
3047 QualType T = QualType(OP.FieldType.getPointer(), 0);
3048
3049 T = S.getASTContext().getPointerType(T);
3050 S.Stk.push<Pointer>(Args: OP.withFieldType(FieldTy: T.getTypePtr(), PastEnd: OP.isOnePastEnd()));
3051 } else {
3052 S.Stk.push<Pointer>(Args: Ptr);
3053 }
3054
3055 return true;
3056}
3057
3058bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC,
3059 const Pointer &Ptr, unsigned BitWidth);
3060bool CheckIntegralAddressCast(InterpState &S, CodePtr OpPC, unsigned BitWidth);
3061bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth);
3062bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth);
3063
3064template <PrimType Name, class T = typename PrimConv<Name>::T>
3065bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
3066 const Pointer &Ptr = S.Stk.pop<Pointer>();
3067 if (!CheckPointerToIntegralCast(S, OpPC, Ptr, T::bitWidth()))
3068 return Invalid(S, OpPC);
3069
3070 if constexpr (std::is_same_v<T, Boolean>) {
3071 S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
3072 } else if constexpr (isIntegralOrPointer<T>()) {
3073 if (Ptr.isBlockPointer()) {
3074 S.Stk.push<T>(IntegralKind::BlockAddress, Ptr.block(), /*Offset=*/0);
3075 } else if (Ptr.isOpaquePointer()) {
3076 if (const Expr *BaseExpr = Ptr.asOpaquePointer().getBaseExpr()) {
3077 IntegralKind Kind = IntegralKind::ExprAddress;
3078 if (isa<AddrLabelExpr>(Val: BaseExpr))
3079 Kind = IntegralKind::LabelAddress;
3080 S.Stk.push<T>(Kind, BaseExpr, 0);
3081 } else {
3082 S.Stk.push<T>(IntegralKind::Address,
3083 Ptr.asOpaquePointer().Base.asVarDecl(), 0);
3084 }
3085
3086 } else if (Ptr.isFunctionPointer()) {
3087 const void *FuncDecl = Ptr.asFunctionPointer().Func->getDecl();
3088 S.Stk.push<T>(IntegralKind::FunctionAddress, FuncDecl, /*Offset=*/0);
3089 } else if (Ptr.isStringPointer()) {
3090 S.Stk.push<T>(IntegralKind::ExprAddress,
3091 (const void *)Ptr.asStringPointer().getLiteral(), 0);
3092 } else {
3093 S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
3094 }
3095 } else {
3096 S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
3097 }
3098 return true;
3099}
3100
3101template <PrimType Name, class T = typename PrimConv<Name>::T>
3102static inline bool CastIntegralFixedPoint(InterpState &S, CodePtr OpPC,
3103 uint32_t FPS) {
3104 const T &Int = S.Stk.pop<T>();
3105
3106 FixedPointSemantics Sem = FixedPointSemantics::getFromOpaqueInt(FPS);
3107
3108 bool Overflow;
3109 FixedPoint Result = FixedPoint::from(Int.toAPSInt(), Sem, &Overflow);
3110
3111 if (Overflow && !handleFixedPointOverflow(S, OpPC, FP: Result))
3112 return false;
3113
3114 S.Stk.push<FixedPoint>(Args&: Result);
3115 return true;
3116}
3117
3118static inline bool CastFloatingFixedPoint(InterpState &S, CodePtr OpPC,
3119 uint32_t FPS) {
3120 const auto &Float = S.Stk.pop<Floating>();
3121
3122 FixedPointSemantics Sem = FixedPointSemantics::getFromOpaqueInt(FPS);
3123
3124 bool Overflow;
3125 FixedPoint Result = FixedPoint::from(I: Float.getAPFloat(), Sem, Overflow: &Overflow);
3126
3127 if (Overflow && !handleFixedPointOverflow(S, OpPC, FP: Result))
3128 return false;
3129
3130 S.Stk.push<FixedPoint>(Args&: Result);
3131 return true;
3132}
3133
3134static inline bool CastFixedPointFloating(InterpState &S,
3135 const llvm::fltSemantics *Sem) {
3136 const auto &Fixed = S.Stk.pop<FixedPoint>();
3137 Floating Result = S.allocFloat(Sem: *Sem);
3138 Result.copy(F: Fixed.toFloat(Sem));
3139 S.Stk.push<Floating>(Args&: Result);
3140 return true;
3141}
3142
3143template <PrimType Name, class T = typename PrimConv<Name>::T>
3144static inline bool CastFixedPointIntegral(InterpState &S, CodePtr OpPC) {
3145 const auto &Fixed = S.Stk.pop<FixedPoint>();
3146
3147 bool Overflow;
3148 APSInt Int = Fixed.toInt(BitWidth: T::bitWidth(), Signed: T::isSigned(), Overflow: &Overflow);
3149
3150 if (Overflow && !handleOverflow(S, OpPC, SrcValue: Int))
3151 return false;
3152
3153 S.Stk.push<T>(Int);
3154 return true;
3155}
3156
3157static inline bool FnPtrCast(InterpState &S, CodePtr OpPC) {
3158 const SourceInfo &E = S.Current->getSource(PC: OpPC);
3159 S.CCEDiag(SI: E, DiagId: diag::note_constexpr_invalid_cast)
3160 << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
3161 << S.getLangOpts().CPlusPlus << S.Current->getRange(PC: OpPC);
3162 return true;
3163}
3164
3165bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr,
3166 const Type *TargetType);
3167
3168//===----------------------------------------------------------------------===//
3169// Zero, Nullptr
3170//===----------------------------------------------------------------------===//
3171
3172template <PrimType Name, class T = typename PrimConv<Name>::T>
3173bool Zero(InterpState &S) {
3174 S.Stk.push<T>(T::zero());
3175 return true;
3176}
3177
3178static inline bool ZeroIntAP(InterpState &S, uint32_t BitWidth) {
3179 auto Result = S.allocAP<IntegralAP<false>>(BitWidth);
3180 if (!Result.singleWord())
3181 std::memset(s: Result.Memory, c: 0, n: Result.numWords() * sizeof(uint64_t));
3182 S.Stk.push<IntegralAP<false>>(Args&: Result);
3183 return true;
3184}
3185
3186static inline bool ZeroIntAPS(InterpState &S, uint32_t BitWidth) {
3187 auto Result = S.allocAP<IntegralAP<true>>(BitWidth);
3188 if (!Result.singleWord())
3189 std::memset(s: Result.Memory, c: 0, n: Result.numWords() * sizeof(uint64_t));
3190 S.Stk.push<IntegralAP<true>>(Args&: Result);
3191 return true;
3192}
3193
3194template <PrimType Name, class T = typename PrimConv<Name>::T>
3195inline bool Null(InterpState &S, uint64_t Value, const Type *Ty) {
3196 // FIXME(perf): This is a somewhat often-used function and the value of a
3197 // null pointer is almost always 0.
3198 if constexpr (std::is_same_v<T, Pointer>)
3199 S.Stk.push<T>(Value, Ty, /*Offset=*/0, /*IsNull=*/true);
3200 else
3201 S.Stk.push<T>(Value, Ty);
3202 return true;
3203}
3204
3205inline bool CastAddressSpace(InterpState &S, CodePtr OpPC, uint64_t Value,
3206 const Type *Ty) {
3207 const Pointer Ptr = S.Stk.pop<Pointer>();
3208 if (Ptr.isZero())
3209 S.Stk.push<Pointer>(Args&: Value, Args&: Ty);
3210 else
3211 S.Stk.push<Pointer>(Args: Ptr);
3212 return true;
3213}
3214
3215template <PrimType Name, class T = typename PrimConv<Name>::T>
3216inline bool IsNonNull(InterpState &S) {
3217 const auto &P = S.Stk.pop<T>();
3218 if (P.isWeak())
3219 return false;
3220 S.Stk.push<Boolean>(Boolean::from(!P.isZero()));
3221 return true;
3222}
3223
3224//===----------------------------------------------------------------------===//
3225// This, ImplicitThis
3226//===----------------------------------------------------------------------===//
3227
3228inline bool This(InterpState &S, CodePtr OpPC) {
3229 // Cannot read 'this' in this mode.
3230 if (S.checkingPotentialConstantExpression())
3231 return false;
3232 if (!CheckThis(S, OpPC))
3233 return false;
3234 const Pointer &This = S.Current->getThis();
3235
3236 // Ensure the This pointer has been cast to the correct base.
3237 if (!This.isDummy()) {
3238 assert(isa<CXXMethodDecl>(S.Current->getFunction()->getDecl()));
3239 if (!This.isTypeidPointer()) {
3240 [[maybe_unused]] const Record *R = This.getRecord();
3241 if (!R)
3242 R = This.narrow().getRecord();
3243 if (!R)
3244 return false;
3245 assert(R->getDecl() ==
3246 cast<CXXMethodDecl>(S.Current->getFunction()->getDecl())
3247 ->getParent());
3248 }
3249 }
3250
3251 S.Stk.push<Pointer>(Args: This);
3252 return true;
3253}
3254
3255inline bool RVOPtr(InterpState &S) {
3256 assert(S.Current->getFunction()->hasRVO());
3257 if (S.checkingPotentialConstantExpression())
3258 return false;
3259 S.Stk.push<Pointer>(Args: S.Current->getRVOPtr());
3260 return true;
3261}
3262
3263//===----------------------------------------------------------------------===//
3264// Shr, Shl
3265//===----------------------------------------------------------------------===//
3266
3267template <class LT, class RT, ShiftDir Dir>
3268inline bool DoShift(InterpState &S, CodePtr OpPC, LT &LHS, RT &RHS,
3269 LT *Result) {
3270 static_assert(!needsAlloc<LT>());
3271 const unsigned Bits = LHS.bitWidth();
3272
3273 // OpenCL 6.3j: shift values are effectively % word size of LHS.
3274 if (S.getLangOpts().OpenCL)
3275 RT::bitAnd(RHS, RT::from(LHS.bitWidth() - 1, RHS.bitWidth()),
3276 RHS.bitWidth(), &RHS);
3277
3278 if (RHS.isNegative()) {
3279 // During constant-folding, a negative shift is an opposite shift. Such a
3280 // shift is not a constant expression.
3281 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
3282 S.CCEDiag(SI: Loc, DiagId: diag::note_constexpr_negative_shift) << RHS.toAPSInt();
3283 if (!S.noteUndefinedBehavior())
3284 return false;
3285
3286 RHS = RHS.isMin() ? RT(APSInt::getMaxValue(numBits: RHS.bitWidth(), Unsigned: false)) : -RHS;
3287
3288 return DoShift<LT, RT,
3289 Dir == ShiftDir::Left ? ShiftDir::Right : ShiftDir::Left>(
3290 S, OpPC, LHS, RHS, Result);
3291 }
3292
3293 if (!CheckShift<Dir>(S, OpPC, LHS, RHS, Bits))
3294 return false;
3295
3296 // Limit the shift amount to Bits - 1. If this happened,
3297 // it has already been diagnosed by CheckShift() above,
3298 // but we still need to handle it.
3299 // Note that we have to be extra careful here since we're doing the shift in
3300 // any case, but we need to adjust the shift amount or the way we do the shift
3301 // for the potential error cases.
3302 typename LT::AsUnsigned R;
3303 unsigned MaxShiftAmount = LHS.bitWidth() - 1;
3304 if constexpr (Dir == ShiftDir::Left) {
3305 if (Compare(RHS, RT::from(MaxShiftAmount, RHS.bitWidth())) ==
3306 ComparisonCategoryResult::Greater) {
3307 if (LHS.isNegative())
3308 R = LT::AsUnsigned::zero(LHS.bitWidth());
3309 else {
3310 RHS = RT::from(LHS.countLeadingZeros(), RHS.bitWidth());
3311 LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
3312 LT::AsUnsigned::from(RHS, Bits), Bits, &R);
3313 }
3314 } else if (LHS.isNegative()) {
3315 if (LHS.isMin()) {
3316 R = LT::AsUnsigned::zero(LHS.bitWidth());
3317 } else {
3318 // If the LHS is negative, perform the cast and invert the result.
3319 typename LT::AsUnsigned LHSU = LT::AsUnsigned::from(-LHS);
3320 LT::AsUnsigned::shiftLeft(LHSU, LT::AsUnsigned::from(RHS, Bits), Bits,
3321 &R);
3322 R = -R;
3323 }
3324 } else {
3325 // The good case, a simple left shift.
3326 LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
3327 LT::AsUnsigned::from(RHS, Bits), Bits, &R);
3328 }
3329 S.Stk.push<LT>(LT::from(R));
3330 return true;
3331 }
3332
3333 // Right shift.
3334 if (Compare(RHS, RT::from(MaxShiftAmount, RHS.bitWidth())) ==
3335 ComparisonCategoryResult::Greater) {
3336 R = LT::AsUnsigned::from(0);
3337 } else {
3338 // Do the shift on potentially signed LT, then convert to unsigned type.
3339 LT A;
3340 LT::shiftRight(LHS, LT::from(RHS, Bits), Bits, &A);
3341 R = LT::AsUnsigned::from(A);
3342 }
3343
3344 S.Stk.push<LT>(LT::from(R));
3345 return true;
3346}
3347
3348/// A version of DoShift that works on IntegralAP.
3349template <class LT, class RT, ShiftDir Dir>
3350inline bool DoShiftAP(InterpState &S, CodePtr OpPC, const APSInt &LHS,
3351 APSInt RHS, LT *Result) {
3352 const unsigned Bits = LHS.getBitWidth();
3353
3354 // OpenCL 6.3j: shift values are effectively % word size of LHS.
3355 if (S.getLangOpts().OpenCL)
3356 RHS &=
3357 APSInt(llvm::APInt(RHS.getBitWidth(), static_cast<uint64_t>(Bits - 1)),
3358 RHS.isUnsigned());
3359
3360 if (RHS.isNegative()) {
3361 // During constant-folding, a negative shift is an opposite shift. Such a
3362 // shift is not a constant expression.
3363 const SourceInfo &Loc = S.Current->getSource(PC: OpPC);
3364 S.CCEDiag(SI: Loc, DiagId: diag::note_constexpr_negative_shift) << RHS; //.toAPSInt();
3365 if (!S.noteUndefinedBehavior())
3366 return false;
3367 return DoShiftAP<LT, RT,
3368 Dir == ShiftDir::Left ? ShiftDir::Right : ShiftDir::Left>(
3369 S, OpPC, LHS, -(RHS.extend(width: RHS.getBitWidth() + 1)), Result);
3370 }
3371
3372 if (!CheckShift<Dir>(S, OpPC, static_cast<LT>(LHS), static_cast<RT>(RHS),
3373 Bits))
3374 return false;
3375
3376 unsigned SA = (unsigned)RHS.getLimitedValue(Limit: Bits - 1);
3377 if constexpr (Dir == ShiftDir::Left) {
3378 if constexpr (needsAlloc<LT>())
3379 Result->copy(LHS << SA);
3380 else
3381 *Result = LT(LHS << SA);
3382 } else {
3383 if constexpr (needsAlloc<LT>())
3384 Result->copy(LHS >> SA);
3385 else
3386 *Result = LT(LHS >> SA);
3387 }
3388
3389 S.Stk.push<LT>(*Result);
3390 return true;
3391}
3392
3393template <PrimType NameL, PrimType NameR>
3394inline bool Shr(InterpState &S, CodePtr OpPC) {
3395 using LT = typename PrimConv<NameL>::T;
3396 using RT = typename PrimConv<NameR>::T;
3397 auto RHS = S.Stk.pop<RT>();
3398 auto LHS = S.Stk.pop<LT>();
3399
3400 if constexpr (needsAlloc<LT>() || needsAlloc<RT>()) {
3401 LT Result;
3402 if constexpr (needsAlloc<LT>())
3403 Result = S.allocAP<LT>(LHS.bitWidth());
3404 return DoShiftAP<LT, RT, ShiftDir::Right>(S, OpPC, LHS.toAPSInt(),
3405 RHS.toAPSInt(), &Result);
3406 } else {
3407 LT Result;
3408 return DoShift<LT, RT, ShiftDir::Right>(S, OpPC, LHS, RHS, &Result);
3409 }
3410}
3411
3412template <PrimType NameL, PrimType NameR>
3413inline bool Shl(InterpState &S, CodePtr OpPC) {
3414 using LT = typename PrimConv<NameL>::T;
3415 using RT = typename PrimConv<NameR>::T;
3416 auto RHS = S.Stk.pop<RT>();
3417 auto LHS = S.Stk.pop<LT>();
3418
3419 if constexpr (needsAlloc<LT>() || needsAlloc<RT>()) {
3420 LT Result;
3421 if constexpr (needsAlloc<LT>())
3422 Result = S.allocAP<LT>(LHS.bitWidth());
3423 return DoShiftAP<LT, RT, ShiftDir::Left>(S, OpPC, LHS.toAPSInt(),
3424 RHS.toAPSInt(), &Result);
3425 } else {
3426 LT Result;
3427 return DoShift<LT, RT, ShiftDir::Left>(S, OpPC, LHS, RHS, &Result);
3428 }
3429}
3430
3431static inline bool ShiftFixedPoint(InterpState &S, CodePtr OpPC, bool Left) {
3432 const auto &RHS = S.Stk.pop<FixedPoint>();
3433 const auto &LHS = S.Stk.pop<FixedPoint>();
3434 llvm::FixedPointSemantics LHSSema = LHS.getSemantics();
3435
3436 unsigned ShiftBitWidth =
3437 LHSSema.getWidth() - (unsigned)LHSSema.hasUnsignedPadding() - 1;
3438
3439 // Embedded-C 4.1.6.2.2:
3440 // The right operand must be nonnegative and less than the total number
3441 // of (nonpadding) bits of the fixed-point operand ...
3442 if (RHS.isNegative()) {
3443 S.CCEDiag(Loc: S.Current->getLocation(PC: OpPC), DiagId: diag::note_constexpr_negative_shift)
3444 << RHS.toAPSInt();
3445 } else if (static_cast<unsigned>(RHS.toAPSInt().getLimitedValue(
3446 Limit: ShiftBitWidth)) != RHS.toAPSInt()) {
3447 const Expr *E = S.Current->getExpr(PC: OpPC);
3448 S.CCEDiag(E, DiagId: diag::note_constexpr_large_shift)
3449 << RHS.toAPSInt() << E->getType() << ShiftBitWidth;
3450 }
3451
3452 FixedPoint Result;
3453 if (Left) {
3454 if (FixedPoint::shiftLeft(A: LHS, B: RHS, OpBits: ShiftBitWidth, R: &Result) &&
3455 !handleFixedPointOverflow(S, OpPC, FP: Result))
3456 return false;
3457 } else {
3458 if (FixedPoint::shiftRight(A: LHS, B: RHS, OpBits: ShiftBitWidth, R: &Result) &&
3459 !handleFixedPointOverflow(S, OpPC, FP: Result))
3460 return false;
3461 }
3462
3463 S.Stk.push<FixedPoint>(Args&: Result);
3464 return true;
3465}
3466
3467//===----------------------------------------------------------------------===//
3468// NoRet
3469//===----------------------------------------------------------------------===//
3470PRESERVE_NONE inline bool NoRet(InterpState &S) {
3471 SourceLocation EndLoc = S.Current->getCallee()->getEndLoc();
3472 S.FFDiag(Loc: EndLoc, DiagId: diag::note_constexpr_no_return);
3473 return false;
3474}
3475
3476//===----------------------------------------------------------------------===//
3477// NarrowPtr, ExpandPtr
3478//===----------------------------------------------------------------------===//
3479
3480inline bool NarrowPtr(InterpState &S) {
3481 const Pointer &Ptr = S.Stk.pop<Pointer>();
3482 S.Stk.push<Pointer>(Args: Ptr.narrow());
3483 return true;
3484}
3485
3486inline bool ExpandPtr(InterpState &S) {
3487 const Pointer &Ptr = S.Stk.pop<Pointer>();
3488 if (Ptr.isBlockPointer())
3489 S.Stk.push<Pointer>(Args: Ptr.expand());
3490 else
3491 S.Stk.push<Pointer>(Args: Ptr);
3492 return true;
3493}
3494
3495// Implementation for ArrayElemPtr and ArrayElemPtrPop ops.
3496template <typename T>
3497inline bool arrayElemPtr(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
3498 const T &Offset) {
3499 if (Ptr.isOpaquePointer()) {
3500 if (S.inConstantContext() && !Offset.isZero() &&
3501 !CheckArray(S, OpPC, Ptr)) {
3502 return false;
3503 }
3504 return arrayElemPtrOpaque(S, OpPC, Ptr, Offset.toAPSInt());
3505 }
3506
3507 if (Offset.isZero()) {
3508 if (const Descriptor *Desc = Ptr.getFieldDesc();
3509 Desc && Desc->isArray() && Ptr.getIndex() == 0) {
3510 S.Stk.push<Pointer>(Args: Ptr.atIndex(Idx: 0).narrow());
3511 return true;
3512 }
3513 S.Stk.push<Pointer>(Args: Ptr.narrow());
3514 return true;
3515 }
3516
3517 assert(!Offset.isZero());
3518
3519 if (std::optional<Pointer> Result =
3520 OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr)) {
3521 S.Stk.push<Pointer>(Args: Result->narrow());
3522 return true;
3523 }
3524 return false;
3525}
3526
3527// 1) Pops an integral value from the stack
3528// 2) Peeks a pointer
3529// 3) Pushes a new pointer that's a narrowed array
3530// element of the peeked pointer with the value
3531// from 1) added as offset.
3532//
3533// This leaves the original pointer on the stack and pushes a new one
3534// with the offset applied and narrowed.
3535template <PrimType Name, class T = typename PrimConv<Name>::T>
3536inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
3537 const T &Offset = S.Stk.pop<T>();
3538 const Pointer &Ptr = S.Stk.peek<Pointer>();
3539
3540 return arrayElemPtr<T>(S, OpPC, Ptr, Offset);
3541}
3542
3543template <PrimType Name, class T = typename PrimConv<Name>::T>
3544inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
3545 const T &Offset = S.Stk.pop<T>();
3546 const Pointer &Ptr = S.Stk.pop<Pointer>();
3547
3548 return arrayElemPtr<T>(S, OpPC, Ptr, Offset);
3549}
3550
3551template <PrimType Name, class T = typename PrimConv<Name>::T>
3552inline bool ArrayElem(InterpState &S, CodePtr OpPC, uint32_t Index) {
3553 const Pointer &Ptr = S.Stk.peek<Pointer>();
3554
3555 if (!CheckLoad(S, OpPC, Ptr))
3556 return false;
3557
3558 assert(Ptr.atIndex(Index).getFieldDesc()->getPrimType() == Name);
3559 S.Stk.push<T>(Ptr.elem<T>(Index));
3560 return true;
3561}
3562
3563template <PrimType Name, class T = typename PrimConv<Name>::T>
3564inline bool ArrayElemPop(InterpState &S, CodePtr OpPC, uint32_t Index) {
3565 const Pointer &Ptr = S.Stk.pop<Pointer>();
3566
3567 if (!CheckLoad(S, OpPC, Ptr))
3568 return false;
3569
3570 assert(Ptr.atIndex(Index).getFieldDesc()->getPrimType() == Name);
3571 S.Stk.push<T>(Ptr.elem<T>(Index));
3572 return true;
3573}
3574
3575template <PrimType Name, class T = typename PrimConv<Name>::T>
3576inline bool CopyArray(InterpState &S, CodePtr OpPC, uint32_t SrcIndex,
3577 uint32_t DestIndex, uint32_t Size) {
3578 const auto &SrcPtr = S.Stk.pop<Pointer>();
3579 const auto &DestPtr = S.Stk.peek<Pointer>();
3580
3581 if (SrcPtr.isDummy() || DestPtr.isDummy())
3582 return false;
3583
3584 if (!SrcPtr.isBlockPointer() || !DestPtr.isBlockPointer())
3585 return false;
3586
3587 const Descriptor *SrcDesc = SrcPtr.getFieldDesc();
3588 const Descriptor *DestDesc = DestPtr.getFieldDesc();
3589 if (!SrcDesc->isPrimitiveArray() || !DestDesc->isPrimitiveArray() ||
3590 SrcDesc->getPrimType() != Name || DestDesc->getPrimType() != Name)
3591 return false;
3592
3593 for (uint32_t I = 0; I != Size; ++I) {
3594 PtrView SP = SrcPtr.view().atIndex(Idx: SrcIndex + I);
3595
3596 if (!CheckLoad(S, OpPC, Ptr: SP))
3597 return false;
3598
3599 DestPtr.elem<T>(DestIndex + I) = SrcPtr.elem<T>(SrcIndex + I);
3600 DestPtr.initializeElement(Index: DestIndex + I);
3601 }
3602 return true;
3603}
3604
3605/// Just takes a pointer and checks if it's an incomplete
3606/// array type.
3607inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
3608 const Pointer &Ptr = S.Stk.pop<Pointer>();
3609
3610 if (Ptr.isZero()) {
3611 S.Stk.push<Pointer>(Args: Ptr);
3612 return true;
3613 }
3614
3615 if (!Ptr.isZeroSizeArray()) {
3616 if (!CheckRange(S, OpPC, Ptr, CSK: CSK_ArrayToPointer))
3617 return false;
3618 }
3619
3620 if (Ptr.isRoot() || !Ptr.isUnknownSizeArray() || !S.inConstantContext()) {
3621 if (Ptr.isBlockPointer()) {
3622 S.Stk.push<Pointer>(Args: Ptr.atIndex(Idx: 0).narrow());
3623 return true;
3624 }
3625
3626 if (Ptr.isStringPointer()) {
3627 S.Stk.push<Pointer>(Args: Ptr.asStringPointer().decay());
3628 return true;
3629 }
3630
3631 if (!Ptr.isOpaquePointer()) {
3632 S.Stk.push<Pointer>(Args: Ptr);
3633 return true;
3634 }
3635
3636 const OpaquePointer &OP = Ptr.asOpaquePointer();
3637 if (!OP.getFieldType()->isArrayType()) {
3638 S.Stk.push<Pointer>(Args: Ptr);
3639 return true;
3640 }
3641
3642 if (OP.isUnknownSizeArray() && OP.PathLength != 0) {
3643 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
3644 DiagId: diag::note_constexpr_unsupported_unsized_array);
3645 S.Stk.push<Pointer>(Args: Ptr);
3646 return true;
3647 }
3648
3649 return arrayElemPtrOpaque(S, OpPC, Ptr,
3650 Index: APSInt(APInt::getZero(numBits: 1), /*IsUnsigned=*/true),
3651 /*AllowReplace=*/AllowReplace: false);
3652 }
3653
3654 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
3655 DiagId: diag::note_constexpr_unsupported_unsized_array);
3656 return false;
3657}
3658
3659inline bool GetFnPtr(InterpState &S, const Function *Func) {
3660 assert(Func);
3661 S.Stk.push<Pointer>(Args&: Func);
3662 return true;
3663}
3664
3665template <PrimType Name, class T = typename PrimConv<Name>::T>
3666inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Type *Ty) {
3667 const T &IntVal = S.Stk.pop<T>();
3668
3669 S.CCEDiag(SI: S.Current->getSource(PC: OpPC), DiagId: diag::note_constexpr_invalid_cast)
3670 << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
3671 << S.getLangOpts().CPlusPlus;
3672
3673 if constexpr (isIntegralOrPointer<T>()) {
3674 if (IntVal.getKind() == IntegralKind::Address) {
3675 if (IntVal.getOffset() != 0)
3676 return Invalid(S, OpPC);
3677 const VarDecl *VD = (const VarDecl *)IntVal.getPtr();
3678 unsigned GlobalIndex = *S.P.getOrCreateGlobal(VD);
3679 S.Stk.push<Pointer>(Args: S.P.getGlobal(Idx: GlobalIndex));
3680 } else if (IntVal.getKind() == IntegralKind::BlockAddress) {
3681 if (IntVal.getOffset() != 0)
3682 return Invalid(S, OpPC);
3683
3684 const Block *B = (const Block *)IntVal.getPtr();
3685 S.Stk.push<Pointer>(Args: const_cast<Block *>(B));
3686 } else if (IntVal.getKind() == IntegralKind::FunctionAddress) {
3687 const Function *F =
3688 S.P.getFunction(F: (const FunctionDecl *)IntVal.getPtr());
3689 S.Stk.push<Pointer>(F, IntVal.getOffset());
3690 } else {
3691 uint64_t NullValue =
3692 S.getASTContext().getTargetNullPointerValue(QT: QualType(Ty, 0));
3693 S.Stk.push<Pointer>(Args: static_cast<uint64_t>(IntVal), Args&: Ty, Args: 0,
3694 Args: static_cast<uint64_t>(IntVal) == NullValue);
3695 }
3696 } else {
3697 S.Stk.push<Pointer>(Args: static_cast<uint64_t>(IntVal), Args&: Ty);
3698 }
3699
3700 return true;
3701}
3702
3703inline bool GetStringPtr(InterpState &S, const Expr *Base) {
3704 S.Stk.push<Pointer>(Args&: Base, Args: S.newStringID());
3705 return true;
3706}
3707
3708bool GetMemberPtr(InterpState &S, const ValueDecl *D);
3709bool GetMemberPtrBase(InterpState &S);
3710bool GetMemberPtrDecl(InterpState &S);
3711bool CopyMemberPtrPath(InterpState &S, const RecordDecl *Entry, bool IsDerived);
3712
3713/// Just emit a diagnostic. The expression that caused emission of this
3714/// op is not valid in a constant context.
3715
3716inline bool Unsupported(InterpState &S, CodePtr OpPC) {
3717 const SourceLocation &Loc = S.Current->getLocation(PC: OpPC);
3718 S.FFDiag(Loc, DiagId: diag::note_constexpr_stmt_expr_unsupported)
3719 << S.Current->getRange(PC: OpPC);
3720 return false;
3721}
3722
3723inline bool PushIgnoreDiags(InterpState &S) {
3724 ++S.DiagIgnoreDepth;
3725 if (S.DiagIgnoreDepth != 1)
3726 return true;
3727 assert(S.PrevDiags == nullptr);
3728 S.PrevDiags = S.getEvalStatus().Diag;
3729 S.PrevDiagsEmitted = S.getEvalStatus().DiagEmitted;
3730 S.getEvalStatus().Diag = nullptr;
3731 assert(!S.diagnosing());
3732 return true;
3733}
3734
3735inline bool PopIgnoreDiags(InterpState &S) {
3736 assert(S.DiagIgnoreDepth != 0);
3737 --S.DiagIgnoreDepth;
3738 if (S.DiagIgnoreDepth == 0) {
3739 S.getEvalStatus().Diag = S.PrevDiags;
3740 S.getEvalStatus().DiagEmitted = S.PrevDiagsEmitted;
3741 S.PrevDiags = nullptr;
3742 }
3743 return true;
3744}
3745
3746inline bool StartSpeculation(InterpState &S) {
3747#ifndef NDEBUG
3748 ++S.SpeculationDepth;
3749#endif
3750 return true;
3751}
3752
3753inline bool StartInit(InterpState &S) {
3754 const Pointer &Ptr = S.Stk.peek<Pointer>();
3755 S.InitializingPtrs.push_back(Elt: Ptr.view());
3756 return true;
3757}
3758
3759inline bool StartFieldInit(InterpState &S, uint32_t FieldOffset) {
3760 const Pointer &Ptr = S.Stk.peek<Pointer>();
3761 S.InitializingPtrs.push_back(Elt: Ptr.view().atField(Offset: FieldOffset));
3762 return true;
3763}
3764
3765inline bool EndInit(InterpState &S) {
3766 S.InitializingPtrs.pop_back();
3767 return true;
3768}
3769
3770// This is special-cased in the tablegen opcode emitter.
3771// Its dispatch function will NOT call InterpNext
3772// and instead simply return true.
3773PRESERVE_NONE inline bool EndSpeculation(InterpState &S) {
3774#ifndef NDEBUG
3775 assert(S.SpeculationDepth != 0);
3776 --S.SpeculationDepth;
3777#endif
3778 return true;
3779}
3780
3781inline bool PushCC(InterpState &S, bool Value) {
3782 S.ConstantContextOverride = Value;
3783 return true;
3784}
3785inline bool PopCC(InterpState &S) {
3786 S.ConstantContextOverride = std::nullopt;
3787 return true;
3788}
3789
3790inline bool PushMSVCCE(InterpState &S) {
3791 // This is a per-frame property.
3792 ++S.Current->MSVCConstexprAllowed;
3793 return true;
3794}
3795
3796inline bool PopMSVCCE(InterpState &S) {
3797 assert(S.Current->MSVCConstexprAllowed >= 1);
3798 // This is a per-frame property.
3799 --S.Current->MSVCConstexprAllowed;
3800 return true;
3801}
3802
3803/// Do nothing and just abort execution.
3804inline bool Error(InterpState &S) { return false; }
3805
3806inline bool SideEffect(InterpState &S) { return S.noteSideEffect(); }
3807
3808/// Abort without a diagnostic if we're checking for a potential constant
3809/// expression and this is not the bottom frame. This is used in constructors to
3810/// allow evaluating their initializers but abort if we encounter anything in
3811/// their body.
3812inline bool CtorCheck(InterpState &S) {
3813 if (S.checkingPotentialConstantExpression() && !S.Current->isBottomFrame())
3814 return false;
3815 return true;
3816}
3817
3818inline bool InvalidStore(InterpState &S, CodePtr OpPC, const Type *T) {
3819 if (S.getLangOpts().CPlusPlus) {
3820 QualType VolatileType = QualType(T, 0).withVolatile();
3821 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
3822 DiagId: diag::note_constexpr_access_volatile_type)
3823 << AK_Assign << VolatileType;
3824 } else {
3825 S.FFDiag(SI: S.Current->getSource(PC: OpPC));
3826 }
3827 return false;
3828}
3829
3830inline bool SizelessVectorElementSize(InterpState &S, CodePtr OpPC) {
3831 if (S.inConstantContext()) {
3832 const SourceRange &ArgRange = S.Current->getRange(PC: OpPC);
3833 const Expr *E = S.Current->getExpr(PC: OpPC);
3834 S.CCEDiag(E, DiagId: diag::note_constexpr_non_const_vectorelements) << ArgRange;
3835 }
3836 return false;
3837}
3838
3839inline bool CheckPseudoDtor(InterpState &S, CodePtr OpPC) {
3840 if (!S.getLangOpts().CPlusPlus20)
3841 S.CCEDiag(SI: S.Current->getSource(PC: OpPC),
3842 DiagId: diag::note_constexpr_pseudo_destructor);
3843 return true;
3844}
3845
3846inline bool Assume(InterpState &S, CodePtr OpPC) {
3847 const auto Val = S.Stk.pop<Boolean>();
3848
3849 if (Val)
3850 return true;
3851
3852 // Else, diagnose.
3853 const SourceLocation &Loc = S.Current->getLocation(PC: OpPC);
3854 S.CCEDiag(Loc, DiagId: diag::note_constexpr_assumption_failed);
3855 return false;
3856}
3857
3858template <PrimType Name, class T = typename PrimConv<Name>::T>
3859inline bool OffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E) {
3860 llvm::SmallVector<int64_t> ArrayIndices;
3861 for (size_t I = 0; I != E->getNumExpressions(); ++I)
3862 ArrayIndices.emplace_back(
3863 Args: static_cast<int64_t>(S.Stk.pop<Integral<64, true>>()));
3864
3865 int64_t Result;
3866 if (!InterpretOffsetOf(S, OpPC, E, ArrayIndices, Result))
3867 return false;
3868
3869 S.Stk.push<T>(T::from(Result));
3870
3871 return true;
3872}
3873
3874template <PrimType Name, class T = typename PrimConv<Name>::T>
3875inline bool CheckNonNullArg(InterpState &S, CodePtr OpPC) {
3876 const T &Arg = S.Stk.peek<T>();
3877 if (!Arg.isZero())
3878 return true;
3879
3880 const SourceLocation &Loc = S.Current->getLocation(PC: OpPC);
3881 S.CCEDiag(Loc, DiagId: diag::note_non_null_attribute_failed);
3882
3883 return false;
3884}
3885
3886void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
3887 const APSInt &Value);
3888
3889template <PrimType Name, class T = typename PrimConv<Name>::T>
3890inline bool CheckEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED) {
3891 assert(ED);
3892 assert(!ED->isFixed());
3893
3894 if (S.inConstantContext()) {
3895 const APSInt Val = S.Stk.peek<T>().toAPSInt();
3896 diagnoseEnumValue(S, OpPC, ED, Value: Val);
3897 }
3898 return true;
3899}
3900
3901/// OldPtr -> Integer -> NewPtr.
3902template <PrimType TIn, PrimType TOut> inline bool DecayPtr(InterpState &S) {
3903 static_assert(isPtrType(T: TIn) && isPtrType(T: TOut));
3904 using FromT = typename PrimConv<TIn>::T;
3905 using ToT = typename PrimConv<TOut>::T;
3906
3907 const FromT &OldPtr = S.Stk.pop<FromT>();
3908
3909 if constexpr (std::is_same_v<FromT, FunctionPointer> &&
3910 std::is_same_v<ToT, Pointer>) {
3911 S.Stk.push<Pointer>(OldPtr.getFunction(), OldPtr.getOffset());
3912 return true;
3913 } else if constexpr (std::is_same_v<FromT, Pointer> &&
3914 std::is_same_v<ToT, FunctionPointer>) {
3915 if (OldPtr.isFunctionPointer()) {
3916 S.Stk.push<FunctionPointer>(OldPtr.asFunctionPointer().getFunction(),
3917 OldPtr.getByteOffset());
3918 return true;
3919 }
3920 }
3921
3922 S.Stk.push<ToT>(ToT(OldPtr.getIntegerRepresentation(), nullptr));
3923 return true;
3924}
3925
3926inline bool CheckDecl(InterpState &S, const VarDecl *VD) {
3927 // An expression E is a core constant expression unless the evaluation of E
3928 // would evaluate one of the following: [C++23] - a control flow that passes
3929 // through a declaration of a variable with static or thread storage duration
3930 // unless that variable is usable in constant expressions.
3931 assert(VD->isLocalVarDecl() &&
3932 VD->isStaticLocal()); // Checked before emitting this.
3933
3934 if (VD == S.EvaluatingDecl)
3935 return true;
3936
3937 if (!VD->isUsableInConstantExpressions(C: S.getASTContext())) {
3938 S.CCEDiag(Loc: VD->getLocation(), DiagId: diag::note_constexpr_static_local)
3939 << (VD->getTSCSpec() == TSCS_unspecified ? 0 : 1) << VD;
3940 return false;
3941 }
3942 return true;
3943}
3944
3945/// Check if the destination array we're initializing can hold the \p NumElems
3946/// elements.
3947inline bool CheckArrayDestSize(InterpState &S, CodePtr OpPC, size_t NumElems) {
3948 if (!CheckArraySize(S, OpPC, NumElems))
3949 return false;
3950
3951 const Pointer &Ptr = S.Stk.peek<Pointer>();
3952 if (!Ptr.isUnknownSizeArray() && NumElems > Ptr.getNumElems()) {
3953 S.FFDiag(SI: S.Current->getSource(PC: OpPC), DiagId: diag::note_constexpr_new_too_small)
3954 << Ptr.getNumElems() << NumElems;
3955 return false;
3956 }
3957
3958 return true;
3959}
3960
3961inline bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
3962 assert(Desc);
3963
3964 if (!CheckDynamicMemoryAllocation(S, OpPC))
3965 return false;
3966
3967 DynamicAllocator &Allocator = S.getAllocator();
3968 Block *B =
3969 Allocator.allocate(D: Desc, EvalID: S.EvalID, AllocForm: DynamicAllocator::Form::NonArray);
3970 assert(B);
3971 S.Stk.push<Pointer>(Args&: B);
3972 return true;
3973}
3974
3975template <PrimType Name, class SizeT = typename PrimConv<Name>::T>
3976inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source,
3977 bool IsNoThrow) {
3978 if (!CheckDynamicMemoryAllocation(S, OpPC))
3979 return false;
3980
3981 SizeT NumElements = S.Stk.pop<SizeT>();
3982 if (!CheckArraySize(S, OpPC, NumElements, primSize(Type: T), IsNoThrow)) {
3983 if (!IsNoThrow)
3984 return false;
3985
3986 // If this failed and is nothrow, just return a null ptr.
3987 S.Stk.push<Pointer>();
3988 return true;
3989 }
3990 if (NumElements.isNegative()) {
3991 if (!IsNoThrow) {
3992 S.FFDiag(SI: S.Current->getSource(PC: OpPC), DiagId: diag::note_constexpr_new_negative)
3993 << NumElements.toDiagnosticString(S.getASTContext());
3994 return false;
3995 }
3996 S.Stk.push<Pointer>();
3997 return true;
3998 }
3999
4000 if (!CheckArraySize(S, OpPC, NumElems: static_cast<uint64_t>(NumElements)))
4001 return false;
4002
4003 DynamicAllocator &Allocator = S.getAllocator();
4004 Block *B = Allocator.allocate(Source, T, NumElements: static_cast<size_t>(NumElements),
4005 EvalID: S.EvalID, AllocForm: DynamicAllocator::Form::Array);
4006 assert(B);
4007 if (NumElements.isZero())
4008 S.Stk.push<Pointer>(Args&: B);
4009 else
4010 S.Stk.push<Pointer>(Args: Pointer(B).atIndex(Idx: 0));
4011 return true;
4012}
4013
4014template <PrimType Name, class SizeT = typename PrimConv<Name>::T>
4015inline bool AllocCN(InterpState &S, CodePtr OpPC, const Descriptor *ElementDesc,
4016 bool IsNoThrow) {
4017 if (!CheckDynamicMemoryAllocation(S, OpPC))
4018 return false;
4019
4020 if (!ElementDesc)
4021 return false;
4022
4023 SizeT NumElements = S.Stk.pop<SizeT>();
4024 if (!CheckArraySize(S, OpPC, NumElements, ElementDesc->getSize(),
4025 IsNoThrow)) {
4026 if (!IsNoThrow)
4027 return false;
4028
4029 // If this failed and is nothrow, just return a null ptr.
4030 S.Stk.push<Pointer>(Args: 0, Args: ElementDesc->getType().getTypePtr(), Args: 0,
4031 /*IsNull=*/Args: true);
4032 return true;
4033 }
4034 if (NumElements.isNegative()) {
4035 if (!IsNoThrow) {
4036 S.FFDiag(SI: S.Current->getSource(PC: OpPC), DiagId: diag::note_constexpr_new_negative)
4037 << NumElements.toDiagnosticString(S.getASTContext());
4038 return false;
4039 }
4040 S.Stk.push<Pointer>();
4041 return true;
4042 }
4043
4044 if (!CheckArraySize(S, OpPC, NumElems: static_cast<uint64_t>(NumElements)))
4045 return false;
4046
4047 DynamicAllocator &Allocator = S.getAllocator();
4048 Block *B = Allocator.allocate(D: ElementDesc, NumElements: static_cast<size_t>(NumElements),
4049 EvalID: S.EvalID, AllocForm: DynamicAllocator::Form::Array);
4050 assert(B);
4051 if (NumElements.isZero())
4052 S.Stk.push<Pointer>(Args&: B);
4053 else
4054 S.Stk.push<Pointer>(Args: Pointer(B).atIndex(Idx: 0));
4055
4056 return true;
4057}
4058
4059bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
4060 bool IsGlobalDelete);
4061
4062static inline bool IsConstantContext(InterpState &S) {
4063 S.Stk.push<Boolean>(Args: Boolean::from(Value: S.inConstantContext()));
4064 return true;
4065}
4066
4067static inline bool CheckAllocations(InterpState &S) {
4068 return S.maybeDiagnoseDanglingAllocations();
4069}
4070
4071/// Check if the initializer and storage types of a placement-new expression
4072/// match.
4073bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
4074 std::optional<uint64_t> ArraySize = std::nullopt);
4075
4076template <PrimType Name, class T = typename PrimConv<Name>::T>
4077bool CheckNewTypeMismatchArray(InterpState &S, CodePtr OpPC, const Expr *E) {
4078 const auto &Size = S.Stk.pop<T>();
4079 return CheckNewTypeMismatch(S, OpPC, E, ArraySize: static_cast<uint64_t>(Size));
4080}
4081bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E);
4082bool CheckPlacementNew(InterpState &S, CodePtr OpPC, const Expr *E);
4083
4084template <PrimType Name, class T = typename PrimConv<Name>::T>
4085inline bool BitCastPrim(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
4086 uint32_t ResultBitWidth, const llvm::fltSemantics *Sem,
4087 const Type *TargetType) {
4088 const Pointer &FromPtr = S.Stk.pop<Pointer>();
4089
4090 if (!CheckLoad(S, OpPC, Ptr: FromPtr))
4091 return false;
4092
4093 if constexpr (std::is_same_v<T, Pointer>) {
4094 if (!TargetType->isNullPtrType()) {
4095 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
4096 DiagId: diag::note_constexpr_bit_cast_invalid_type)
4097 << /*IsToType=*/true << /*IsReference=*/false << 1 /*Pointer*/;
4098 return false;
4099 }
4100 // The only pointer type we can validly bitcast to is nullptr_t.
4101 S.Stk.push<Pointer>();
4102 return true;
4103 } else if constexpr (std::is_same_v<T, MemberPointer>) {
4104 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
4105 DiagId: diag::note_constexpr_bit_cast_invalid_type)
4106 << /*IsToType=*/true << /*IsReference=*/false << 2 /*MemberPointer*/;
4107 return false;
4108 } else {
4109
4110 size_t BuffSize = ResultBitWidth / 8;
4111 llvm::SmallVector<std::byte> Buff(BuffSize);
4112 bool HasIndeterminateBits = false;
4113
4114 Bits FullBitWidth(ResultBitWidth);
4115 Bits BitWidth = FullBitWidth;
4116
4117 if constexpr (std::is_same_v<T, Floating>) {
4118 assert(Sem);
4119 BitWidth = Bits(llvm::APFloatBase::getSizeInBits(Sem: *Sem));
4120 }
4121
4122 if (!DoBitCast(S, OpPC, Ptr: FromPtr, Buff: Buff.data(), BitWidth, FullBitWidth,
4123 HasIndeterminateBits))
4124 return false;
4125
4126 if (!CheckBitCast(S, OpPC, HasIndeterminateBits, TargetIsUCharOrByte))
4127 return false;
4128
4129 if constexpr (std::is_same_v<T, Floating>) {
4130 assert(Sem);
4131 Floating Result = S.allocFloat(Sem: *Sem);
4132 Floating::bitcastFromMemory(Buff: Buff.data(), Sem: *Sem, Result: &Result);
4133 S.Stk.push<Floating>(Args&: Result);
4134 } else if constexpr (needsAlloc<T>()) {
4135 T Result = S.allocAP<T>(ResultBitWidth);
4136 T::bitcastFromMemory(Buff.data(), ResultBitWidth, &Result);
4137 S.Stk.push<T>(Result);
4138 } else if constexpr (std::is_same_v<T, Boolean>) {
4139 // Only allow to cast single-byte integers to bool if they are either 0
4140 // or 1.
4141 assert(FullBitWidth.getQuantity() == 8);
4142 auto Val = static_cast<unsigned int>(Buff[0]);
4143 if (Val > 1) {
4144 S.FFDiag(SI: S.Current->getSource(PC: OpPC),
4145 DiagId: diag::note_constexpr_bit_cast_unrepresentable_value)
4146 << S.getASTContext().BoolTy << Val;
4147 return false;
4148 }
4149 S.Stk.push<T>(T::bitcastFromMemory(Buff.data(), ResultBitWidth));
4150 } else {
4151 assert(!Sem);
4152 S.Stk.push<T>(T::bitcastFromMemory(Buff.data(), ResultBitWidth));
4153 }
4154 return true;
4155 }
4156}
4157
4158inline bool BitCast(InterpState &S, CodePtr OpPC) {
4159 Pointer FromPtr = S.Stk.pop<Pointer>();
4160 Pointer &ToPtr = S.Stk.peek<Pointer>();
4161
4162 // FIXME: Could allow reading from string pointers?
4163 if (!FromPtr.isBlockPointer() || !ToPtr.isBlockPointer())
4164 return false;
4165
4166 const Descriptor *D = FromPtr.getFieldDesc();
4167 if (D->isPrimitiveArray() && FromPtr.isArrayRoot())
4168 FromPtr = FromPtr.atIndex(Idx: 0);
4169
4170 if (!CheckLoad(S, OpPC, Ptr: FromPtr))
4171 return false;
4172
4173 if (!DoBitCastPtr(S, OpPC, FromPtr, ToPtr))
4174 return false;
4175
4176 return true;
4177}
4178
4179/// Typeid support.
4180bool GetTypeid(InterpState &S, const Type *TypePtr, const Type *TypeInfoType);
4181bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType);
4182bool DiagTypeid(InterpState &S, CodePtr OpPC);
4183
4184inline bool CheckDestruction(InterpState &S, CodePtr OpPC) {
4185 const auto &Ptr = S.Stk.peek<Pointer>();
4186 return checkDestructor(S, OpPC, Ptr);
4187}
4188
4189inline bool ReflectValue(InterpState &S, CodePtr OpPC, ReflectionKind Kind,
4190 const void *Operand) {
4191 S.Stk.push<Reflect>(Args&: Kind, Args&: Operand);
4192 return true;
4193}
4194
4195inline bool IsBaseClass(InterpState &S) {
4196 S.Stk.push<bool>(Args: S.Stk.peek<Pointer>().isBaseClass());
4197 return true;
4198}
4199
4200//===----------------------------------------------------------------------===//
4201// Read opcode arguments
4202//===----------------------------------------------------------------------===//
4203
4204template <typename T> inline T ReadArg(InterpState &S, CodePtr &OpPC) {
4205 if constexpr (std::is_pointer<T>::value)
4206 return reinterpret_cast<T>(OpPC.read<uintptr_t>());
4207 else
4208 return OpPC.read<T>();
4209}
4210
4211template <> inline Floating ReadArg<Floating>(InterpState &S, CodePtr &OpPC) {
4212 auto &Semantics =
4213 llvm::APFloatBase::EnumToSemantics(S: Floating::deserializeSemantics(Buff: *OpPC));
4214
4215 auto F = S.allocFloat(Sem: Semantics);
4216 Floating::deserialize(Buff: *OpPC, Result: &F);
4217 OpPC += align(Size: F.bytesToSerialize());
4218 return F;
4219}
4220
4221template <>
4222inline IntegralAP<false> ReadArg<IntegralAP<false>>(InterpState &S,
4223 CodePtr &OpPC) {
4224 uint32_t BitWidth = IntegralAP<false>::deserializeSize(Buff: *OpPC);
4225 auto Result = S.allocAP<IntegralAP<false>>(BitWidth);
4226 assert(Result.bitWidth() == BitWidth);
4227
4228 IntegralAP<false>::deserialize(Buff: *OpPC, Result: &Result);
4229 OpPC += align(Size: Result.bytesToSerialize());
4230 return Result;
4231}
4232
4233template <>
4234inline IntegralAP<true> ReadArg<IntegralAP<true>>(InterpState &S,
4235 CodePtr &OpPC) {
4236 uint32_t BitWidth = IntegralAP<true>::deserializeSize(Buff: *OpPC);
4237 auto Result = S.allocAP<IntegralAP<true>>(BitWidth);
4238 assert(Result.bitWidth() == BitWidth);
4239
4240 IntegralAP<true>::deserialize(Buff: *OpPC, Result: &Result);
4241 OpPC += align(Size: Result.bytesToSerialize());
4242 return Result;
4243}
4244
4245template <>
4246inline FixedPoint ReadArg<FixedPoint>(InterpState &S, CodePtr &OpPC) {
4247 FixedPoint FP = FixedPoint::deserialize(Buff: *OpPC);
4248 OpPC += align(Size: FP.bytesToSerialize());
4249 return FP;
4250}
4251
4252} // namespace interp
4253} // namespace clang
4254
4255#endif
4256