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

source code of clang/lib/AST/ByteCode/Interp.h