//===------- Interp.cpp - Interpreter for the constexpr VM ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "Interp.h"
#include "Compiler.h"
#include "Function.h"
#include "InterpFrame.h"
#include "InterpShared.h"
#include "InterpStack.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
#include "State.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/StringExtras.h"

using namespace clang;
using namespace clang::interp;

#if __has_cpp_attribute(clang::musttail)
#define MUSTTAIL [[clang::musttail]]
#elif __has_cpp_attribute(msvc::musttail)
#define MUSTTAIL [[msvc::musttail]]
#elif __has_attribute(musttail)
#define MUSTTAIL __attribute__((musttail))
#endif

// On MSVC, musttail does not guarantee tail calls in debug mode.
// We disable it on MSVC generally since it doesn't seem to be able
// to handle the way we use tailcalls.
// PPC can't tail-call external calls, which is a problem for InterpNext.
#if defined(_MSC_VER) || defined(__powerpc__) || !defined(MUSTTAIL) ||         \
    defined(__i386__) || defined(__sparc__)
#undef MUSTTAIL
#define MUSTTAIL
#define USE_TAILCALLS 0
#else
#define USE_TAILCALLS 1
#endif

PRESERVE_NONE static bool RetValue(InterpState &S) {
  llvm::report_fatal_error("Interpreter cannot return values");
}

//===----------------------------------------------------------------------===//
// Jmp, Jt, Jf
//===----------------------------------------------------------------------===//

static bool Jmp(InterpState &S, CodePtr OpPC, int32_t Offset) {
  S.PC += Offset;
  return S.noteStep(OpPC);
}

static bool Jt(InterpState &S, CodePtr OpPC, int32_t Offset) {
  if (S.Stk.pop<bool>()) {
    S.PC += Offset;
    return S.noteStep(OpPC);
  }
  return true;
}

static bool Jf(InterpState &S, CodePtr OpPC, int32_t Offset) {
  if (!S.Stk.pop<bool>()) {
    S.PC += Offset;
    return S.noteStep(OpPC);
  }
  return true;
}

static void diagnoseMissingInitializer(InterpState &S, CodePtr OpPC,
                                       const ValueDecl *VD) {
  const SourceInfo &E = S.Current->getSource(OpPC);
  S.FFDiag(E, diag::note_constexpr_var_init_unknown, 1) << VD;
  S.Note(VD->getLocation(), diag::note_declared_at) << VD->getSourceRange();
}

static void noteValueLocation(InterpState &S, const Block *B) {
  const Descriptor *Desc = B->getDescriptor();

  if (B->isDynamic())
    S.Note(Desc->getLocation(), diag::note_constexpr_dynamic_alloc_here);
  else if (B->isTemporary())
    S.Note(Desc->getLocation(), diag::note_constexpr_temporary_here);
  else
    S.Note(Desc->getLocation(), diag::note_declared_at);
}

static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
                                     const ValueDecl *VD,
                                     AccessKinds AK = AK_Read);
static bool diagnoseUnknownDecl(InterpState &S, CodePtr OpPC,
                                const ValueDecl *D, AccessKinds AK = AK_Read) {
  // This function tries pretty hard to produce a good diagnostic. Just skip
  // that if nobody will see it anyway.
  if (!S.diagnosing())
    return false;

  if (isa<ParmVarDecl>(D)) {
    if (D->getType()->isReferenceType()) {
      if (S.inConstantContext() && S.getLangOpts().CPlusPlus &&
          !S.getLangOpts().CPlusPlus11) {
        diagnoseNonConstVariable(S, OpPC, D);
        return false;
      }
    }

    const SourceInfo &Loc = S.Current->getSource(OpPC);
    if (S.getLangOpts().CPlusPlus23 && D->getType()->isReferenceType()) {
      S.FFDiag(Loc, diag::note_constexpr_access_unknown_variable, 1)
          << AK_Read << D;
      S.Note(D->getLocation(), diag::note_declared_at) << D->getSourceRange();
    } else if (S.getLangOpts().CPlusPlus11) {
      S.FFDiag(Loc, diag::note_constexpr_function_param_value_unknown, 1) << D;
      S.Note(D->getLocation(), diag::note_declared_at) << D->getSourceRange();
    } else {
      S.FFDiag(Loc);
    }
    return false;
  }

  if (!D->getType().isConstQualified()) {
    diagnoseNonConstVariable(S, OpPC, D, AK);
  } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
    if (!VD->getAnyInitializer()) {
      diagnoseMissingInitializer(S, OpPC, VD);
    } else {
      const SourceInfo &Loc = S.Current->getSource(OpPC);
      S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
      S.Note(VD->getLocation(), diag::note_declared_at);
    }
  }

  return false;
}

static bool isModification(AccessKinds AK) {
  return AK == AK_Assign || AK == AK_Increment || AK == AK_Decrement ||
         AK == AK_Construct || AK == AK_Destroy;
}

static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
                                     const ValueDecl *VD, AccessKinds AK) {
  if (!S.diagnosing())
    return;

  const SourceInfo &Loc = S.Current->getSource(OpPC);
  if (!S.getLangOpts().CPlusPlus) {
    S.FFDiag(Loc);
    return;
  }

  if (const auto *VarD = dyn_cast<VarDecl>(VD);
      VarD && VarD->getType().isConstQualified() &&
      (VarD->isConstexpr() || !VarD->getType()->isArrayType()) &&
      !VarD->getAnyInitializer()) {
    diagnoseMissingInitializer(S, OpPC, VD);
    return;
  }

  // Rather random, but this is to match the diagnostic output of the current
  // interpreter.
  if (isa<ObjCIvarDecl>(VD))
    return;

  if (VD->getType()->isIntegralOrEnumerationType()) {
    if (isModification(AK)) {
      S.FFDiag(Loc, diag::note_constexpr_modify_global);
    } else {
      S.FFDiag(Loc, diag::note_constexpr_ltor_non_const_int, 1) << VD;
      S.Note(VD->getLocation(), diag::note_declared_at);
    }
    return;
  }

  S.FFDiag(Loc,
           S.getLangOpts().CPlusPlus11 ? diag::note_constexpr_ltor_non_constexpr
                                       : diag::note_constexpr_ltor_non_integral,
           1)
      << VD << VD->getType();
  S.Note(VD->getLocation(), diag::note_declared_at);
}

static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Block *B,
                           AccessKinds AK) {
  if (B->getDeclID()) {
    if (!(B->isStatic() && B->isTemporary()))
      return true;

    const auto *MTE = dyn_cast_if_present<MaterializeTemporaryExpr>(
        B->getDescriptor()->asExpr());
    if (!MTE)
      return true;

    // FIXME(perf): Since we do this check on every Load from a static
    // temporary, it might make sense to cache the value of the
    // isUsableInConstantExpressions call.
    if (S.checkingConstantDestruction() ||
        (B->getEvalID() != S.EvalID &&
         !MTE->isUsableInConstantExpressions(S.getASTContext()))) {
      const SourceInfo &E = S.Current->getSource(OpPC);
      S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
      noteValueLocation(S, B);
      return false;
    }
  }

  return true;
}

static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
  if (auto ID = Ptr.getDeclID()) {
    if (!Ptr.isStatic())
      return true;

    if (S.P.getCurrentDecl() == ID)
      return true;

    S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_modify_global);
    return false;
  }
  return true;
}

namespace clang {
namespace interp {
PRESERVE_NONE static bool BCP(InterpState &S, CodePtr OpPC, int32_t Offset,
                              PrimType PT);

bool diagnoseShiftFailure(InterpState &S, CodePtr OpPC, ShiftFailure Failure,
                          const APSInt *Value, unsigned Bits) {
  switch (Failure) {
  case ShiftFailure::NegativeCount:
    assert(Value);
    S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_negative_shift)
        << *Value;
    break;
  case ShiftFailure::TooLarge: {
    assert(Value);
    const Expr *E = S.Current->getExpr(OpPC);
    S.CCEDiag(E, diag::note_constexpr_large_shift)
        << *Value << E->getType() << Bits;
    break;
  }
  case ShiftFailure::NegativeLeftOperand:
    assert(Value);
    S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_of_negative)
        << *Value;
    break;
  case ShiftFailure::DiscardsBits:
    S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards);
    break;
  }
  return S.noteUndefinedBehavior();
}

void cleanupAfterFunctionCall(InterpState &S, const Function *Func) {
  assert(S.Current);
  assert(Func);

  // Pop variadic parameter values from the stack.
  if (S.Current->Caller && Func->isVariadic()) {
    unsigned VariadicArgSize =
        S.Current->getArgSize() - S.Current->getFunction()->getArgSize();
    unsigned TargetStackSize = S.Stk.size() - VariadicArgSize;
    while (S.Stk.size() != TargetStackSize) {
      S.Stk.discardSlow();
    }
  }

  // And in any case, remove the fixed parameters (the non-variadic ones)
  // at the end.
  for (const Function::ParamDescriptor &PDesc : Func->args_reverse())
    TYPE_SWITCH(PDesc.T, S.Stk.discard<T>());

  if (Func->hasImplicitThisPointer())
    S.Stk.discard<Pointer>();
  if (Func->hasRVO())
    S.Stk.discard<Pointer>();
}

bool isConstexprUnknown(const Block *B) {
  if (B->isDummy())
    return isa_and_nonnull<ParmVarDecl>(B->getDescriptor()->asValueDecl());
  return B->getDescriptor()->IsConstexprUnknown;
}

bool isConstexprUnknown(const Pointer &P) {
  if (!P.isBlockPointer() || P.isZero())
    return false;
  return isConstexprUnknown(P.block());
}

bool CheckBCPResult(InterpState &S, const Pointer &Ptr) {
  if (Ptr.isDummy())
    return false;
  if (Ptr.isZero())
    return true;
  if (Ptr.isFunctionPointer())
    return false;
  if (Ptr.isIntegralPointer())
    return true;
  if (Ptr.isTypeidPointer())
    return true;

  if (Ptr.getType()->isAnyComplexType())
    return true;

  if (const Expr *Base = Ptr.getDeclDesc()->asExpr())
    return isa<StringLiteral>(Base) && Ptr.getIndex() == 0;
  return false;
}

bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                 AccessKinds AK, bool WillActivate) {
  if (Ptr.isActive())
    return true;

  assert(Ptr.inUnion());

  // Find the outermost union.
  PtrView U = Ptr.view().getBase();
  PtrView C = Ptr.view();
  while (!U.isRoot() && !U.isActive()) {
    // A little arbitrary, but this is what the current interpreter does.
    // See the AnonymousUnion test in test/AST/ByteCode/unions.cpp.
    // GCC's output is more similar to what we would get without
    // this condition.
    if (U.getRecord() && U.getRecord()->isAnonymousUnion())
      break;

    C = U;
    U = U.getBase();
  }
  assert(C.isField());
  assert(C.getBase() == U);

  // Consider:
  // union U {
  //   struct {
  //     int x;
  //     int y;
  //   } a;
  // }
  //
  // When activating x, we will also activate a. If we now try to read
  // from y, we will get to CheckActive, because y is not active. In that
  // case, our U will be a (not a union). We return here and let later code
  // handle this.
  if (!U.getFieldDesc()->isUnion())
    return true;

  // When we will activate Ptr, check that none of the unions in its path have a
  // non-trivial default constructor.
  if (WillActivate) {
    bool Fails = false;
    PtrView It = Ptr.view();
    while (!It.isRoot() && !It.isActive()) {
      if (const Record *R = It.getRecord(); R && R->isUnion()) {
        if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(R->getDecl());
            CXXRD && !CXXRD->hasTrivialDefaultConstructor()) {
          Fails = true;
          break;
        }
      }
      It = It.getBase();
    }
    if (!Fails)
      return true;
  }

  // Get the inactive field descriptor.
  assert(!C.isActive());
  const FieldDecl *InactiveField = C.getField();
  assert(InactiveField);

  // Find the active field of the union.
  const Record *R = U.getRecord();
  assert(R && R->isUnion() && "Not a union");

  const FieldDecl *ActiveField = nullptr;
  for (const Record::Field &F : R->fields()) {
    PtrView Field = U.atField(F.Offset);
    if (Field.isActive()) {
      ActiveField = Field.getField();
      break;
    }
  }

  S.FFDiag(S.Current->getSource(OpPC),
           diag::note_constexpr_access_inactive_union_member)
      << AK << InactiveField << !ActiveField << ActiveField;
  return false;
}

bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
  if (!Ptr.isExtern())
    return true;

  if (!Ptr.isPastEnd() &&
      (Ptr.isInitialized() ||
       (Ptr.getDeclDesc()->asVarDecl() == S.EvaluatingDecl)))
    return true;

  if (S.checkingPotentialConstantExpression() && S.getLangOpts().CPlusPlus &&
      Ptr.isConst())
    return false;

  const auto *VD = Ptr.getDeclDesc()->asValueDecl();
  if (!Ptr.isConstexprUnknown() || !S.checkingPotentialConstantExpression())
    diagnoseNonConstVariable(S, OpPC, VD);
  return false;
}

bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
  if (!Ptr.isUnknownSizeArray())
    return true;
  const SourceInfo &E = S.Current->getSource(OpPC);
  S.FFDiag(E, diag::note_constexpr_unsized_array_indexed);
  return false;
}

bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
               AccessKinds AK) {
  if (Ptr.isZero()) {
    const auto &Src = S.Current->getSource(OpPC);

    if (Ptr.isField())
      S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field;
    else
      S.FFDiag(Src, diag::note_constexpr_access_null) << AK;

    return false;
  }

  if (!Ptr.isLive()) {
    const auto &Src = S.Current->getSource(OpPC);

    if (Ptr.isDynamic()) {
      S.FFDiag(Src, diag::note_constexpr_access_deleted_object) << AK;
    } else if (!S.checkingPotentialConstantExpression()) {
      S.FFDiag(Src, diag::note_constexpr_access_uninit)
          << AK << /*uninitialized=*/false << S.Current->getRange(OpPC);
      noteValueLocation(S, Ptr.block());
    }

    return false;
  }

  return true;
}

bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc,
                   AccessKinds AK) {
  assert(Desc);

  const auto *D = Desc->asVarDecl();
  if (S.checkingConstantDestruction(D)) {
    // If we're checking for a constant destructor for this variable, we can
    // only read from it if it is constant.
    if (D->getType().isConstQualified())
      return true;
  } else if (!D || D == S.EvaluatingDecl || D->isConstexpr())
    return true;

  // If we're evaluating the initializer for a constexpr variable in C23, we may
  // only read other contexpr variables. Abort here since this one isn't
  // constexpr.
  if (const auto *VD = dyn_cast_if_present<VarDecl>(S.EvaluatingDecl);
      VD && VD->isConstexpr() && S.getLangOpts().C23)
    return Invalid(S, OpPC);

  QualType T = D->getType();
  bool IsConstant = T.isConstant(S.getASTContext());
  if (T->isIntegralOrEnumerationType()) {
    if (!IsConstant) {
      diagnoseNonConstVariable(S, OpPC, D, AK);
      return false;
    }
    return true;
  }

  if (IsConstant) {
    if (S.getLangOpts().CPlusPlus) {
      S.CCEDiag(S.Current->getLocation(OpPC),
                S.getLangOpts().CPlusPlus11
                    ? diag::note_constexpr_ltor_non_constexpr
                    : diag::note_constexpr_ltor_non_integral,
                1)
          << D << T;
      S.Note(D->getLocation(), diag::note_declared_at);
    } else {
      S.CCEDiag(S.Current->getLocation(OpPC));
    }
    return true;
  }

  if (T->isPointerOrReferenceType()) {
    if (!T->getPointeeType().isConstant(S.getASTContext()) ||
        !S.getLangOpts().CPlusPlus11) {
      diagnoseNonConstVariable(S, OpPC, D, AK);
      return false;
    }
    return true;
  }

  diagnoseNonConstVariable(S, OpPC, D, AK);
  return false;
}

static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                          AccessKinds AK = AK_Read) {
  if (S.checkingConstantDestruction(Ptr))
    return CheckConstant(S, OpPC, Ptr.getDeclDesc(), AK);

  if (!Ptr.isStatic() || !Ptr.isBlockPointer())
    return true;
  if (!Ptr.getDeclID())
    return true;
  return CheckConstant(S, OpPC, Ptr.getDeclDesc(), AK);
}

bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
               CheckSubobjectKind CSK) {
  if (!Ptr.isZero())
    return true;
  const SourceInfo &Loc = S.Current->getSource(OpPC);
  S.FFDiag(Loc, diag::note_constexpr_null_subobject)
      << CSK << S.Current->getRange(OpPC);

  return false;
}

bool CheckRange(InterpState &S, CodePtr OpPC, PtrView Ptr, AccessKinds AK) {
  if (!Ptr.isOnePastEnd() && !Ptr.isZeroSizeArray())
    return true;
  if (S.getLangOpts().CPlusPlus) {
    const SourceInfo &Loc = S.Current->getSource(OpPC);
    S.FFDiag(Loc, diag::note_constexpr_access_past_end)
        << AK << S.Current->getRange(OpPC);
  }
  return false;
}

bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                CheckSubobjectKind CSK) {
  if (!Ptr.isElementPastEnd() && !Ptr.isZeroSizeArray())
    return true;
  const SourceInfo &Loc = S.Current->getSource(OpPC);
  S.FFDiag(Loc, diag::note_constexpr_past_end_subobject)
      << CSK << S.Current->getRange(OpPC);
  return false;
}

bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                    CheckSubobjectKind CSK) {
  if (!Ptr.isOnePastEnd())
    return true;

  const SourceInfo &Loc = S.Current->getSource(OpPC);
  S.FFDiag(Loc, diag::note_constexpr_past_end_subobject)
      << CSK << S.Current->getRange(OpPC);
  return false;
}

bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                   uint32_t Offset) {
  uint32_t MinOffset = Ptr.getDeclDesc()->getMetadataSize();
  uint32_t PtrOffset = Ptr.getByteOffset();

  // We subtract Offset from PtrOffset. The result must be at least
  // MinOffset.
  if (Offset < PtrOffset && (PtrOffset - Offset) >= MinOffset)
    return true;

  const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
  QualType ExprTy = E->getType();
  if (ExprTy->isPointerOrReferenceType())
    ExprTy = ExprTy->getPointeeType();

  QualType TargetQT = ExprTy;
  QualType MostDerivedQT = Ptr.getDeclPtr().getType();

  if (MostDerivedQT->isPointerOrReferenceType())
    MostDerivedQT = MostDerivedQT->getPointeeType();

  S.CCEDiag(E, diag::note_constexpr_invalid_downcast)
      << MostDerivedQT << TargetQT;

  return false;
}

bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
  assert(Ptr.isLive() && "Pointer is not live");
  if (!Ptr.isConst())
    return true;

  if (Ptr.isMutable() && !Ptr.isConstInMutable())
    return true;

  if (!Ptr.isBlockPointer())
    return false;

  // The This pointer is writable in constructors and destructors,
  // even if isConst() returns true.
  for (PtrView V : llvm::reverse(S.InitializingPtrs)) {
    if (V.block() != Ptr.block())
      continue;
    if (!V.getFieldDesc()->IsConst) {
      // If the pointer being initialized is not declared as const,
      // Ptr is const because of a parent of V, but that is irrelevant
      // since V is being initialized and NOT const.
      // This is fine, so return true.
      return true;
    }

    // We know that Ptr is const because of a parent field and we also
    // know that V is explicitly marked const.
    // But since V is in InitializingPtrs, the fact that it is const doesn't
    // matter and it is writable.
    // What we now need to check is whether there is a pointer between Ptr and V
    // that is marked const but NOT in InitializingPtrs. If that is the case,
    // Ptr is currently not writable.
    bool FoundProblem = false;
    for (PtrView P = Ptr.view(); P != V; P = P.getBase()) {
      if (P.getFieldDesc()->IsConst) {
        FoundProblem = true;
        break;
      }
    }

    // We couldn't find any pointer that's explicitly marked const, so
    // Ptr is writable right now.
    if (!FoundProblem)
      return true;
    // We only need to find the right block once.
    break;
  }

  if (!S.checkingPotentialConstantExpression()) {
    QualType Ty = Ptr.getType();
    if (!Ptr.getFieldDesc()->IsConst)
      Ty.addConst();
    const SourceInfo &Loc = S.Current->getSource(OpPC);
    S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty;
  }
  return false;
}

bool CheckMutable(InterpState &S, CodePtr OpPC, PtrView Ptr, AccessKinds AK) {
  assert(Ptr.isLive() && "Pointer is not live");
  if (!Ptr.isMutable())
    return true;

  if (S.checkingConstantDestruction()) {
    // Never allowed when checking for constant destruction.
    // Diagnose below.
  } else if (S.getLangOpts().CPlusPlus14 &&
             S.lifetimeStartedInEvaluation(Ptr.block())) {
    // In C++14 onwards, it is permitted to read a mutable member whose
    // lifetime began within the evaluation.
    return true;
  }

  // Find the reason this pointer is mutable.
  PtrView MutablePtr = Ptr;
  while (!MutablePtr.isRoot() && MutablePtr.getBase().isMutable())
    MutablePtr = MutablePtr.getBase();

  const SourceInfo &Loc = S.Current->getSource(OpPC);
  const FieldDecl *Field = MutablePtr.getField();
  S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK << Field;
  S.Note(Field->getLocation(), diag::note_declared_at);
  return false;
}

static bool CheckVolatile(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                          AccessKinds AK) {
  assert(Ptr.isLive());

  if (!Ptr.isVolatile())
    return true;

  if (!S.getLangOpts().CPlusPlus)
    return Invalid(S, OpPC);

  // Volatile object can be written-to and read if they are being constructed.
  if (S.initializingBlock(Ptr.block()))
    return true;

  // The reason why Ptr is volatile might be further up the hierarchy.
  // Find that pointer.
  Pointer P = Ptr;
  while (!P.isRoot()) {
    if (P.getType().isVolatileQualified())
      break;
    P = P.getBase();
  }

  const NamedDecl *ND = nullptr;
  int DiagKind;
  SourceLocation Loc;
  if (const auto *F = P.getField()) {
    DiagKind = 2;
    Loc = F->getLocation();
    ND = F;
  } else if (auto *VD = P.getFieldDesc()->asValueDecl()) {
    DiagKind = 1;
    Loc = VD->getLocation();
    ND = VD;
  } else {
    DiagKind = 0;
    if (const auto *E = P.getFieldDesc()->asExpr())
      Loc = E->getExprLoc();
  }

  S.FFDiag(S.Current->getLocation(OpPC),
           diag::note_constexpr_access_volatile_obj, 1)
      << AK << DiagKind << ND;
  S.Note(Loc, diag::note_constexpr_volatile_here) << DiagKind;
  return false;
}

bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                           AccessKinds AK) {
  assert(Ptr.isLive());
  assert(!Ptr.isInitialized());
  return DiagnoseUninitialized(S, OpPC, Ptr.isExtern(), Ptr.block(), AK);
}

bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
                           const Block *B, AccessKinds AK) {
  if (S.checkingPotentialConstantExpression()) {
    // Extern and static member declarations might be initialized later.
    if (Extern)
      return false;

    if (const VarDecl *VD = B->getDescriptor()->asVarDecl();
        VD && VD->isStaticDataMember())
      return false;
  }

  const Descriptor *Desc = B->getDescriptor();

  if (const auto *VD = Desc->asVarDecl();
      VD && (VD->isConstexpr() || VD->hasGlobalStorage())) {

    if (VD == S.EvaluatingDecl &&
        !(S.getLangOpts().CPlusPlus23 && VD->getType()->isReferenceType())) {
      if (!S.getLangOpts().CPlusPlus14 &&
          !VD->getType().isConstant(S.getASTContext())) {
        // Diagnose as non-const read.
        diagnoseNonConstVariable(S, OpPC, VD);
      } else {
        const SourceInfo &Loc = S.Current->getSource(OpPC);
        // Diagnose as "read of object outside its lifetime".
        S.FFDiag(Loc, diag::note_constexpr_access_uninit)
            << AK << /*IsIndeterminate=*/false;
        S.Note(VD->getLocation(), diag::note_declared_at);
      }
      return false;
    }

    if (VD->getAnyInitializer()) {
      const SourceInfo &Loc = S.Current->getSource(OpPC);
      S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
      S.Note(VD->getLocation(), diag::note_declared_at);
    } else {
      diagnoseMissingInitializer(S, OpPC, VD);
    }
    return false;
  }

  if (!S.checkingPotentialConstantExpression()) {
    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_uninit)
        << AK << /*uninitialized=*/true << S.Current->getRange(OpPC);
    noteValueLocation(S, B);
  }
  return false;
}

static bool CheckLifetime(InterpState &S, CodePtr OpPC, Lifetime LT,
                          const Block *B, AccessKinds AK) {
  if (LT == Lifetime::Started)
    return true;

  if (!S.checkingPotentialConstantExpression()) {
    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_uninit)
        << AK << /*uninitialized=*/false << S.Current->getRange(OpPC);
    noteValueLocation(S, B);
  }
  return false;
}
static bool CheckLifetime(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                          AccessKinds AK) {
  return CheckLifetime(S, OpPC, Ptr.getLifetime(), Ptr.block(), AK);
}

static bool CheckWeak(InterpState &S, CodePtr OpPC, const Block *B) {
  if (!B->isWeak())
    return true;

  const auto *VD = B->getDescriptor()->asVarDecl();
  assert(VD);
  S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_var_init_weak)
      << VD;
  S.Note(VD->getLocation(), diag::note_declared_at);

  return false;
}

// The list of checks here is just the one from CheckLoad, but with the
// ones removed that are impossible on primitive global values.
// For example, since those can't be members of structs, they also can't
// be mutable.
bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
  const auto &Desc = B->getBlockDesc<GlobalInlineDescriptor>();
  if (!B->isAccessible()) {
    if (!CheckExtern(S, OpPC, Pointer(const_cast<Block *>(B))))
      return false;
    if (!CheckDummy(S, OpPC, B, AK_Read))
      return false;
    return CheckWeak(S, OpPC, B);
  }

  if (!CheckConstant(S, OpPC, B->getDescriptor()))
    return false;
  if (Desc.InitState != GlobalInitState::Initialized)
    return DiagnoseUninitialized(S, OpPC, B->isExtern(), B, AK_Read);
  if (!CheckTemporary(S, OpPC, B, AK_Read))
    return false;
  if (B->getDescriptor()->IsVolatile) {
    if (!S.getLangOpts().CPlusPlus)
      return Invalid(S, OpPC);

    const ValueDecl *D = B->getDescriptor()->asValueDecl();
    S.FFDiag(S.Current->getLocation(OpPC),
             diag::note_constexpr_access_volatile_obj, 1)
        << AK_Read << 1 << D;
    S.Note(D->getLocation(), diag::note_constexpr_volatile_here) << 1;
    return false;
  }
  return true;
}

// Similarly, for local loads.
bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
  assert(!B->isExtern());
  const auto &Desc = *reinterpret_cast<const InlineDescriptor *>(B->rawData());
  const Descriptor *BlockDesc = B->getDescriptor();
  if (!CheckLifetime(S, OpPC, Desc.LifeState, B, AK_Read))
    return false;
  if (!Desc.IsInitialized)
    return DiagnoseUninitialized(S, OpPC, /*Extern=*/false, B, AK_Read);
  if (BlockDesc->IsVolatile) {
    if (!S.getLangOpts().CPlusPlus)
      return Invalid(S, OpPC);

    const ValueDecl *D = BlockDesc->asValueDecl();
    S.FFDiag(S.Current->getLocation(OpPC),
             diag::note_constexpr_access_volatile_obj, 1)
        << AK_Read << 1 << D;
    S.Note(D->getLocation(), diag::note_constexpr_volatile_here) << 1;
    return false;
  }

  // A non-const local variable while we don't have a parent frame. This must be
  // a local variable in a statement expression.
  if (S.Current->isBottomFrame() && !BlockDesc->IsConst &&
      !BlockDesc->IsTemporary && !S.checkingPotentialConstantExpression()) {
    if (const ValueDecl *VD = BlockDesc->asValueDecl())
      diagnoseNonConstVariable(S, OpPC, VD);
    return false;
  }
  return true;
}

bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
               AccessKinds AK) {
  if (Ptr.isZero()) {
    const auto &Src = S.Current->getSource(OpPC);

    if (Ptr.isField())
      S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field;
    else
      S.FFDiag(Src, diag::note_constexpr_access_null) << AK;
    return false;
  }
  // Block pointers are the only ones we can actually read from.
  if (!Ptr.isBlockPointer())
    return false;

  if (!Ptr.block()->isAccessible()) {
    if (!CheckLive(S, OpPC, Ptr, AK))
      return false;
    if (!CheckExtern(S, OpPC, Ptr))
      return false;
    if (!CheckDummy(S, OpPC, Ptr.block(), AK))
      return false;
    return CheckWeak(S, OpPC, Ptr.block());
  }

  if (!CheckConstant(S, OpPC, Ptr, AK))
    return false;
  if (!CheckRange(S, OpPC, Ptr, AK))
    return false;
  if (!CheckActive(S, OpPC, Ptr, AK))
    return false;
  if (!Ptr.isInitialized())
    return DiagnoseUninitialized(S, OpPC, Ptr, AK);
  if (!CheckLifetime(S, OpPC, Ptr, AK))
    return false;
  if (!CheckTemporary(S, OpPC, Ptr.block(), AK))
    return false;

  if (!CheckMutable(S, OpPC, Ptr))
    return false;
  if (!CheckVolatile(S, OpPC, Ptr, AK))
    return false;
  if (isConstexprUnknown(Ptr))
    return false;

  if (!Ptr.isArrayRoot()) {
    // According to GCC info page:
    //
    // 6.28 Compound Literals
    //
    // As an optimization, G++ sometimes gives array compound literals
    // longer lifetimes: when the array either appears outside a function or
    // has a const-qualified type. If foo and its initializer had elements
    // of type char *const rather than char *, or if foo were a global
    // variable, the array would have static storage duration. But it is
    // probably safest just to avoid the use of array compound literals in
    // C++ code.
    //
    // Obey that rule by checking constness for converted array types.
    const Descriptor *Desc = Ptr.getFieldDesc();
    if (const auto *CLE =
            dyn_cast_if_present<CompoundLiteralExpr>(Desc->asExpr())) {
      if (QualType CLETy = CLE->getType();
          CLETy->isArrayType() && !CLETy.isConstant(S.getASTContext())) {
        S.FFDiag(S.Current->getLocation(OpPC),
                 diag::note_invalid_subexpr_in_const_expr)
            << S.Current->getRange(OpPC);
        S.Note(CLE->getExprLoc(), diag::note_declared_at);
        return false;
      }
    }
  }
  return true;
}

/// This is not used by any of the opcodes directly. It's used by
/// EvalEmitter to do the final lvalue-to-rvalue conversion.
bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
  assert(!Ptr.isZero());
  if (!Ptr.isBlockPointer())
    return false;

  if (!Ptr.block()->isAccessible()) {
    if (!CheckLive(S, OpPC, Ptr, AK_Read))
      return false;
    if (!CheckExtern(S, OpPC, Ptr))
      return false;
    if (!CheckDummy(S, OpPC, Ptr.block(), AK_Read))
      return false;
    return CheckWeak(S, OpPC, Ptr.block());
  }

  if (!CheckConstant(S, OpPC, Ptr))
    return false;

  if (!CheckActive(S, OpPC, Ptr, AK_Read))
    return false;
  if (!CheckLifetime(S, OpPC, Ptr, AK_Read))
    return false;
  if (!Ptr.isInitialized())
    return DiagnoseUninitialized(S, OpPC, Ptr, AK_Read);
  if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Read))
    return false;
  if (!CheckMutable(S, OpPC, Ptr))
    return false;
  if (Ptr.isConstexprUnknown())
    return false;
  return true;
}

bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                bool WillBeActivated) {
  if (!Ptr.isBlockPointer() || Ptr.isZero())
    return false;

  if (!Ptr.block()->isAccessible()) {
    if (!CheckLive(S, OpPC, Ptr, AK_Assign))
      return false;
    if (!CheckExtern(S, OpPC, Ptr))
      return false;
    return CheckDummy(S, OpPC, Ptr.block(), AK_Assign);
  }
  if (!WillBeActivated && !CheckLifetime(S, OpPC, Ptr, AK_Assign))
    return false;
  if (!CheckRange(S, OpPC, Ptr, AK_Assign))
    return false;
  if (!CheckActive(S, OpPC, Ptr, AK_Assign, WillBeActivated))
    return false;
  if (!CheckGlobal(S, OpPC, Ptr))
    return false;
  if (!CheckConst(S, OpPC, Ptr))
    return false;
  if (!CheckVolatile(S, OpPC, Ptr, AK_Assign))
    return false;
  if (!CheckMutable(S, OpPC, Ptr, AK_Assign))
    return false;
  if (isConstexprUnknown(Ptr))
    return false;
  return true;
}

static bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                        bool IsCtor, bool IsDtor) {
  if (!Ptr.isDummy() && !isConstexprUnknown(Ptr)) {
    if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
      return false;
    if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
      return false;
    if (!(IsCtor || IsDtor) && !CheckLifetime(S, OpPC, Ptr, AK_MemberCall))
      return false;
  }
  return true;
}

bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
  if (!CheckLive(S, OpPC, Ptr, AK_Assign))
    return false;
  if (!CheckRange(S, OpPC, Ptr, AK_Assign))
    return false;
  return true;
}

static bool diagnoseCallableDecl(InterpState &S, CodePtr OpPC,
                                 const FunctionDecl *DiagDecl) {
  // Bail out if the function declaration itself is invalid.  We will
  // have produced a relevant diagnostic while parsing it, so just
  // note the problematic sub-expression.
  if (DiagDecl->isInvalidDecl())
    return Invalid(S, OpPC);

  // Diagnose failed assertions specially.
  if (S.Current->getLocation(OpPC).isMacroID() && DiagDecl->getIdentifier()) {
    // FIXME: Instead of checking for an implementation-defined function,
    // check and evaluate the assert() macro.
    StringRef Name = DiagDecl->getName();
    bool AssertFailed =
        Name == "__assert_rtn" || Name == "__assert_fail" || Name == "_wassert";
    if (AssertFailed) {
      S.FFDiag(S.Current->getLocation(OpPC),
               diag::note_constexpr_assert_failed);
      return false;
    }
  }

  if (!S.getLangOpts().CPlusPlus11) {
    S.FFDiag(S.Current->getLocation(OpPC),
             diag::note_invalid_subexpr_in_const_expr);
    return false;
  }

  // Invalid decls have been diagnosed before.
  if (DiagDecl->isInvalidDecl())
    return false;

  // If this function is not constexpr because it is an inherited
  // non-constexpr constructor, diagnose that directly.
  const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
  if (CD && CD->isInheritingConstructor()) {
    const auto *Inherited = CD->getInheritedConstructor().getConstructor();
    if (!Inherited->isConstexpr())
      DiagDecl = CD = Inherited;
  }

  // Silently reject constructors of invalid classes. The invalid class
  // has been rejected elsewhere before.
  if (CD && CD->getParent()->isInvalidDecl())
    return false;

  // FIXME: If DiagDecl is an implicitly-declared special member function
  // or an inheriting constructor, we should be much more explicit about why
  // it's not constexpr.
  if (CD && CD->isInheritingConstructor()) {
    S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_invalid_inhctor,
             1)
        << CD->getInheritedConstructor().getConstructor()->getParent();
    S.Note(DiagDecl->getLocation(), diag::note_declared_at);
  } else {
    // Don't emit anything if the function isn't defined and we're checking
    // for a constant expression. It might be defined at the point we're
    // actually calling it.
    bool IsExtern = DiagDecl->getStorageClass() == SC_Extern;
    bool IsDefined = DiagDecl->isDefined();
    if (!IsDefined && !IsExtern && DiagDecl->isConstexpr() &&
        S.checkingPotentialConstantExpression())
      return false;

    // If the declaration is defined, declared 'constexpr' _and_ has a body,
    // the below diagnostic doesn't add anything useful.
    if (DiagDecl->isDefined() && DiagDecl->isConstexpr() && DiagDecl->hasBody())
      return false;

    S.FFDiag(S.Current->getLocation(OpPC),
             diag::note_constexpr_invalid_function, 1)
        << DiagDecl->isConstexpr() << (bool)CD << DiagDecl;

    const FunctionDecl *Definition;
    const Stmt *Body = DiagDecl->getBody(Definition);
    if (Body && Definition)
      S.Note(Definition->getLocation(), diag::note_declared_at);
    else
      S.Note(DiagDecl->getLocation(), diag::note_declared_at);
  }

  return false;
}

static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
  if (F->isVirtual() && !S.getLangOpts().CPlusPlus20) {
    const SourceLocation &Loc = S.Current->getLocation(OpPC);
    S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
    return false;
  }

  if (F->isValid() && F->hasBody() &&
      (F->isConstexpr() || (S.Current->MSVCConstexprAllowed &&
                            F->getDecl()->hasAttr<MSConstexprAttr>())))
    return true;

  const FunctionDecl *DiagDecl = F->getDecl();
  const FunctionDecl *Definition = nullptr;
  DiagDecl->getBody(Definition);

  if (!Definition && S.checkingPotentialConstantExpression() &&
      DiagDecl->isConstexpr()) {
    return false;
  }

  // Implicitly constexpr.
  if (F->isLambdaStaticInvoker())
    return true;

  return diagnoseCallableDecl(S, OpPC, DiagDecl);
}

static bool CheckCallDepth(InterpState &S, CodePtr OpPC) {
  if ((S.Current->getDepth() + 1) > S.getLangOpts().ConstexprCallDepth) {
    S.FFDiag(S.Current->getSource(OpPC),
             diag::note_constexpr_depth_limit_exceeded)
        << S.getLangOpts().ConstexprCallDepth;
    return false;
  }

  return true;
}

bool CheckThis(InterpState &S, CodePtr OpPC) {
  if (S.Current->hasThisPointer())
    return true;

  const Expr *E = S.Current->getExpr(OpPC);
  if (S.getLangOpts().CPlusPlus11) {
    bool IsImplicit = false;
    if (const auto *TE = dyn_cast<CXXThisExpr>(E))
      IsImplicit = TE->isImplicit();
    S.FFDiag(E, diag::note_constexpr_this) << IsImplicit;
  } else {
    S.FFDiag(E);
  }

  return false;
}

bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
                      APFloat::opStatus Status, FPOptions FPO) {
  // [expr.pre]p4:
  //   If during the evaluation of an expression, the result is not
  //   mathematically defined [...], the behavior is undefined.
  // FIXME: C++ rules require us to not conform to IEEE 754 here.
  if (Result.isNan()) {
    const SourceInfo &E = S.Current->getSource(OpPC);
    S.CCEDiag(E, diag::note_constexpr_float_arithmetic)
        << /*NaN=*/true << S.Current->getRange(OpPC);
    return S.noteUndefinedBehavior();
  }

  // In a constant context, assume that any dynamic rounding mode or FP
  // exception state matches the default floating-point environment.
  if (S.inConstantContext())
    return true;

  if ((Status & APFloat::opInexact) &&
      FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
    // Inexact result means that it depends on rounding mode. If the requested
    // mode is dynamic, the evaluation cannot be made in compile time.
    const SourceInfo &E = S.Current->getSource(OpPC);
    S.FFDiag(E, diag::note_constexpr_dynamic_rounding);
    return false;
  }

  if ((Status != APFloat::opOK) &&
      (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic ||
       FPO.getExceptionMode() != LangOptions::FPE_Ignore ||
       FPO.getAllowFEnvAccess())) {
    const SourceInfo &E = S.Current->getSource(OpPC);
    S.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
    return false;
  }

  if ((Status & APFloat::opStatus::opInvalidOp) &&
      FPO.getExceptionMode() != LangOptions::FPE_Ignore) {
    const SourceInfo &E = S.Current->getSource(OpPC);
    // There is no usefully definable result.
    S.FFDiag(E);
    return false;
  }

  return true;
}

bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC) {
  if (S.getLangOpts().CPlusPlus20)
    return true;

  const SourceInfo &E = S.Current->getSource(OpPC);
  S.CCEDiag(E, diag::note_constexpr_new);
  return true;
}

bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC,
                         DynamicAllocator::Form AllocForm,
                         DynamicAllocator::Form DeleteForm, const Descriptor *D,
                         const Expr *NewExpr) {
  if (AllocForm == DeleteForm)
    return true;

  QualType TypeToDiagnose = D->getDataType(S.getASTContext());

  const SourceInfo &E = S.Current->getSource(OpPC);
  S.FFDiag(E, diag::note_constexpr_new_delete_mismatch)
      << static_cast<int>(DeleteForm) << static_cast<int>(AllocForm)
      << TypeToDiagnose;
  S.Note(NewExpr->getExprLoc(), diag::note_constexpr_dynamic_alloc_here)
      << NewExpr->getSourceRange();
  return false;
}

bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
                       const Pointer &Ptr) {
  // Regular new type(...) call.
  if (isa_and_nonnull<CXXNewExpr>(Source))
    return true;
  // operator new.
  if (const auto *CE = dyn_cast_if_present<CallExpr>(Source);
      CE && CE->getBuiltinCallee() == Builtin::BI__builtin_operator_new)
    return true;
  // std::allocator.allocate() call
  if (const auto *MCE = dyn_cast_if_present<CXXMemberCallExpr>(Source);
      MCE && MCE->getMethodDecl()->getIdentifier()->isStr("allocate"))
    return true;

  // Whatever this is, we didn't heap allocate it.
  const SourceInfo &Loc = S.Current->getSource(OpPC);
  S.FFDiag(Loc, diag::note_constexpr_delete_not_heap_alloc)
      << Ptr.toDiagnosticString(S.getASTContext());
  noteValueLocation(S, Ptr.block());
  return false;
}

/// We aleady know the given DeclRefExpr is invalid for some reason,
/// now figure out why and print appropriate diagnostics.
bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR) {
  const ValueDecl *D = DR->getDecl();
  return diagnoseUnknownDecl(S, OpPC, D);
}

bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR,
                    bool InitializerFailed) {
  assert(DR);

  if (InitializerFailed) {
    const SourceInfo &Loc = S.Current->getSource(OpPC);
    const auto *VD = cast<VarDecl>(DR->getDecl());
    S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
    S.Note(VD->getLocation(), diag::note_declared_at);
    return false;
  }

  return CheckDeclRef(S, OpPC, DR);
}

bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK) {
  if (!B->isDummy())
    return true;

  const ValueDecl *D = B->getDescriptor()->asValueDecl();
  if (!D)
    return false;

  if (AK == AK_Read || AK == AK_Increment || AK == AK_Decrement)
    return diagnoseUnknownDecl(S, OpPC, D, AK);

  if (AK == AK_Destroy || S.getLangOpts().CPlusPlus14) {
    const SourceInfo &E = S.Current->getSource(OpPC);
    S.FFDiag(E, diag::note_constexpr_modify_global);
  }
  return false;
}

static bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
                             const CallExpr *CE, unsigned ArgSize) {
  auto Args = ArrayRef(CE->getArgs(), CE->getNumArgs());
  auto NonNullArgs = collectNonNullArgs(F->getDecl(), Args);
  unsigned Offset = 0;
  unsigned Index = 0;
  for (const Expr *Arg : Args) {
    if (NonNullArgs[Index] && Arg->getType()->isPointerType()) {
      const Pointer &ArgPtr = S.Stk.peek<Pointer>(ArgSize - Offset);
      if (ArgPtr.isZero()) {
        const SourceLocation &Loc = S.Current->getLocation(OpPC);
        S.CCEDiag(Loc, diag::note_non_null_attribute_failed);
        return false;
      }
    }

    Offset += align(primSize(S.Ctx.classify(Arg).value_or(PT_Ptr)));
    ++Index;
  }
  return true;
}

static bool runRecordDestructor(InterpState &S, CodePtr OpPC,
                                const Pointer &BasePtr,
                                const Descriptor *Desc) {
  assert(Desc->isRecord());
  const Record *R = Desc->ElemRecord;
  assert(R);

  if (!S.Current->isBottomFrame() && S.Current->hasThisPointer() &&
      S.Current->getFunction()->isDestructor() &&
      Pointer::pointToSameBlock(BasePtr, S.Current->getThis())) {
    const SourceInfo &Loc = S.Current->getSource(OpPC);
    S.FFDiag(Loc, diag::note_constexpr_double_destroy);
    return false;
  }

  // Destructor of this record.
  const CXXDestructorDecl *Dtor = R->getDestructor();
  assert(Dtor);
  assert(!Dtor->isTrivial());
  const Function *DtorFunc = S.getContext().getOrCreateFunction(Dtor);
  if (!DtorFunc)
    return false;

  S.Stk.push<Pointer>(BasePtr);
  return Call(S, OpPC, DtorFunc, 0);
}

static bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) {
  assert(B);
  const Descriptor *Desc = B->getDescriptor();

  if (Desc->isPrimitive() || Desc->isPrimitiveArray())
    return true;

  assert(Desc->isRecord() || Desc->isCompositeArray());

  if (Desc->hasTrivialDtor())
    return true;

  if (Desc->isCompositeArray()) {
    unsigned N = Desc->getNumElems();
    if (N == 0)
      return true;
    const Descriptor *ElemDesc = Desc->ElemDesc;
    assert(ElemDesc->isRecord());

    Pointer RP(const_cast<Block *>(B));
    for (int I = static_cast<int>(N) - 1; I >= 0; --I) {
      if (!runRecordDestructor(S, OpPC, RP.atIndex(I).narrow(), ElemDesc))
        return false;
    }
    return true;
  }

  assert(Desc->isRecord());
  return runRecordDestructor(S, OpPC, Pointer(const_cast<Block *>(B)), Desc);
}

static bool hasVirtualDestructor(QualType T) {
  if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
    if (const CXXDestructorDecl *DD = RD->getDestructor())
      return DD->isVirtual();
  return false;
}

bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
          bool IsGlobalDelete) {
  if (!CheckDynamicMemoryAllocation(S, OpPC))
    return false;

  DynamicAllocator &Allocator = S.getAllocator();

  const Expr *Source = nullptr;
  const Block *BlockToDelete = nullptr;
  {
    // Extra scope for this so the block doesn't have this pointer
    // pointing to it when we destroy it.
    Pointer Ptr = S.Stk.pop<Pointer>();

    // Deleteing nullptr is always fine.
    if (Ptr.isZero())
      return true;

    if (!Ptr.isBlockPointer())
      return false;

    // Remove base casts.
    QualType InitialType = Ptr.getType();
    Ptr = Ptr.expand().stripBaseCasts();

    Source = Ptr.getDeclDesc()->asExpr();
    BlockToDelete = Ptr.block();

    // Check that new[]/delete[] or new/delete were used, not a mixture.
    const Descriptor *BlockDesc = BlockToDelete->getDescriptor();
    if (std::optional<DynamicAllocator::Form> AllocForm =
            Allocator.getAllocationForm(Source)) {
      DynamicAllocator::Form DeleteForm =
          DeleteIsArrayForm ? DynamicAllocator::Form::Array
                            : DynamicAllocator::Form::NonArray;
      if (!CheckNewDeleteForms(S, OpPC, *AllocForm, DeleteForm, BlockDesc,
                               Source))
        return false;
    }

    // For the non-array case, the types must match if the static type
    // does not have a virtual destructor.
    if (!DeleteIsArrayForm && Ptr.getType() != InitialType &&
        !hasVirtualDestructor(InitialType)) {
      S.FFDiag(S.Current->getSource(OpPC),
               diag::note_constexpr_delete_base_nonvirt_dtor)
          << InitialType << Ptr.getType();
      return false;
    }

    if (!Ptr.isRoot() || (Ptr.isOnePastEnd() && !Ptr.isZeroSizeArray()) ||
        (Ptr.isArrayElement() && Ptr.getIndex() != 0)) {
      const SourceInfo &Loc = S.Current->getSource(OpPC);
      S.FFDiag(Loc, diag::note_constexpr_delete_subobject)
          << Ptr.toDiagnosticString(S.getASTContext()) << Ptr.isOnePastEnd();
      return false;
    }

    if (!CheckDeleteSource(S, OpPC, Source, Ptr))
      return false;

    // For a class type with a virtual destructor, the selected operator delete
    // is the one looked up when building the destructor.
    if (!DeleteIsArrayForm && !IsGlobalDelete) {
      QualType AllocType = Ptr.getType();
      auto getVirtualOperatorDelete = [](QualType T) -> const FunctionDecl * {
        if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
          if (const CXXDestructorDecl *DD = RD->getDestructor())
            return DD->isVirtual() ? DD->getOperatorDelete() : nullptr;
        return nullptr;
      };

      if (const FunctionDecl *VirtualDelete =
              getVirtualOperatorDelete(AllocType);
          VirtualDelete &&
          !VirtualDelete
               ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
        S.FFDiag(S.Current->getSource(OpPC),
                 diag::note_constexpr_new_non_replaceable)
            << isa<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
        return false;
      }
    }
  }
  assert(Source);
  assert(BlockToDelete);

  // Invoke destructors before deallocating the memory.
  if (!RunDestructors(S, OpPC, BlockToDelete))
    return false;

  if (!Allocator.deallocate(Source, BlockToDelete)) {
    // Nothing has been deallocated, this must be a double-delete.
    const SourceInfo &Loc = S.Current->getSource(OpPC);
    S.FFDiag(Loc, diag::note_constexpr_double_delete);
    return false;
  }

  return true;
}

void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
                       const APSInt &Value) {
  llvm::APInt Min;
  llvm::APInt Max;
  ED->getValueRange(Max, Min);
  --Max;

  if (ED->getNumNegativeBits() &&
      (Max.slt(Value.getSExtValue()) || Min.sgt(Value.getSExtValue()))) {
    const SourceLocation &Loc = S.Current->getLocation(OpPC);
    S.CCEDiag(Loc, diag::note_constexpr_unscoped_enum_out_of_range)
        << llvm::toString(Value, 10) << Min.getSExtValue() << Max.getSExtValue()
        << ED;
  } else if (!ED->getNumNegativeBits() && Max.ult(Value.getZExtValue())) {
    const SourceLocation &Loc = S.Current->getLocation(OpPC);
    S.CCEDiag(Loc, diag::note_constexpr_unscoped_enum_out_of_range)
        << llvm::toString(Value, 10) << Min.getZExtValue() << Max.getZExtValue()
        << ED;
  }
}

bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T) {
  assert(T);
  assert(!S.getLangOpts().CPlusPlus23);

  // C++1y: A constant initializer for an object o [...] may also invoke
  // constexpr constructors for o and its subobjects even if those objects
  // are of non-literal class types.
  //
  // C++11 missed this detail for aggregates, so classes like this:
  //   struct foo_t { union { int i; volatile int j; } u; };
  // are not (obviously) initializable like so:
  //   __attribute__((__require_constant_initialization__))
  //   static const foo_t x = {{0}};
  // because "i" is a subobject with non-literal initialization (due to the
  // volatile member of the union). See:
  //   http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1677
  // Therefore, we use the C++1y behavior.

  if (!S.Current->isBottomFrame() &&
      S.Current->getFunction()->isConstructor() &&
      S.Current->getThis().getDeclDesc()->asDecl() == S.EvaluatingDecl) {
    return true;
  }

  const Expr *E = S.Current->getExpr(OpPC);
  if (S.getLangOpts().CPlusPlus11)
    S.FFDiag(E, diag::note_constexpr_nonliteral) << E->getType();
  else
    S.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
  return false;
}

static bool diagnoseTypeIdField(InterpState &S, CodePtr OpPC,
                                const Pointer &Ptr, unsigned Offset) {
  assert(Ptr.isTypeidPointer());
  const Record *R = S.getContext().getRecord(
      Ptr.asTypeidPointer().TypeInfoType->getAsRecordDecl());
  if (!R)
    return false;
  const Record::Field *Field =
      llvm::find_if(R->fields(), [=](const Record::Field &F) -> bool {
        return F.Offset == Offset;
      });
  if (!Field)
    return false;

  std::string TypeIdStr;
  llvm::raw_string_ostream SS(TypeIdStr);
  SS << "typeid(";
  QualType(Ptr.asTypeidPointer().TypePtr, 0)
      .print(SS, S.getASTContext().getPrintingPolicy());
  SS << ").";
  SS << Field->Decl->getNameAsString();

  S.FFDiag(S.Current->getSource(OpPC),
           diag::note_constexpr_access_unreadable_object)
      << AK_Read << TypeIdStr;
  return false;
}

static bool getField(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                     uint32_t Off) {
  if (S.getLangOpts().CPlusPlus && S.inConstantContext() &&
      !CheckNull(S, OpPC, Ptr, CSK_Field))
    return false;

  if (!CheckRange(S, OpPC, Ptr, CSK_Field))
    return false;
  if (!CheckArray(S, OpPC, Ptr))
    return false;
  if (!CheckSubobject(S, OpPC, Ptr, CSK_Field))
    return false;

  if (Ptr.isIntegralPointer()) {
    if (std::optional<IntPointer> IntPtr =
            Ptr.asIntPointer().atOffset(S.Ctx, Off)) {
      S.Stk.push<Pointer>(std::move(*IntPtr));
      return true;
    }
    return false;
  }

  if (!Ptr.isBlockPointer()) {
    // If we're trying to get the field of a TypeId pointer, try to produce a
    // proper diagnostic.
    if (Ptr.isTypeidPointer())
      return diagnoseTypeIdField(S, OpPC, Ptr, Off);
    return false;
  }

  // We can't get the field of something that's not a record.
  if (!Ptr.getFieldDesc()->isRecord())
    return false;

  if ((Ptr.getByteOffset() + Off) >= Ptr.block()->getSize())
    return false;

  S.Stk.push<Pointer>(Ptr.atField(Off));
  return true;
}

bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
  const auto &Ptr = S.Stk.peek<Pointer>();
  return getField(S, OpPC, Ptr, Off);
}

bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
  const auto &Ptr = S.Stk.pop<Pointer>();
  return getField(S, OpPC, Ptr, Off);
}

static bool getBase(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                    uint32_t Off, bool NullOK) {
  if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Base))
    return false;

  if (!Ptr.isBlockPointer()) {
    if (!Ptr.isIntegralPointer())
      return false;
    S.Stk.push<Pointer>(Ptr.asIntPointer().baseCast(S.Ctx, Off));
    return true;
  }

  if (!CheckSubobject(S, OpPC, Ptr, CSK_Base))
    return false;

  // In case this isn't something we can get the base of at all,
  // just return the pointer itself so it can be diagnosed later.
  if (!Ptr.getFieldDesc()->isRecord()) {
    S.Stk.push<Pointer>(Ptr);
    return true;
  }

  const Pointer &Result = Ptr.atField(Off);
  if (Result.isPastEnd() || !Result.isBaseClass())
    return false;
  S.Stk.push<Pointer>(Result);
  return true;
}

bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
  const auto &Ptr = S.Stk.peek<Pointer>();
  return getBase(S, OpPC, Ptr.narrow(), Off, /*NullOK=*/true);
}
bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK) {
  const auto &Ptr = S.Stk.pop<Pointer>();
  return getBase(S, OpPC, Ptr.narrow(), Off, NullOK);
}

bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK,
                      const Type *TargetType) {
  const Pointer &Ptr = S.Stk.pop<Pointer>().narrow();
  if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Derived))
    return false;

  if (!Ptr.isBlockPointer()) {
    // FIXME: We don't have the necessary information in integral pointers.
    // The Descriptor only has a record, but that does of course not include
    // the potential derived classes of said record.
    S.Stk.push<Pointer>(Ptr);
    return true;
  }

  if (!Ptr.getFieldDesc()->isRecord()) {
    S.Stk.push<Pointer>(Ptr);
    return true;
  }

  if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
    return false;
  if (!CheckDowncast(S, OpPC, Ptr, Off))
    return false;

  const Record *TargetRecord = Ptr.atFieldSub(Off).getRecord();
  assert(TargetRecord);

  if (TargetRecord->getDecl()->getCanonicalDecl() !=
      TargetType->getAsCXXRecordDecl()->getCanonicalDecl()) {
    QualType MostDerivedType = Ptr.getDeclDesc()->getType();
    S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_downcast)
        << MostDerivedType << QualType(TargetType, 0);
    return false;
  }

  S.Stk.push<Pointer>(Ptr.atFieldSub(Off));
  return true;
}

static bool checkConstructor(InterpState &S, CodePtr OpPC, const Function *Func,
                             const Pointer &ThisPtr) {
  assert(Func->isConstructor());

  if (Func->getParentDecl()->isInvalidDecl())
    return false;

  const Descriptor *D = ThisPtr.getFieldDesc();
  // FIXME: I think this case is not 100% correct. E.g. a pointer into a
  // subobject of a composite array.
  if (!D->ElemRecord)
    return true;

  if (D->ElemRecord->getNumVirtualBases() == 0)
    return true;

  S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_virtual_base)
      << Func->getParentDecl();
  return false;
}

static bool diagnoseOutOfLifetimeDestroy(InterpState &S, CodePtr OpPC,
                                         const Pointer &Ptr) {
  assert(Ptr.getLifetime() != Lifetime::Started);
  // Try to use the declaration for better diagnostics
  if (const Decl *D = Ptr.getDeclDesc()->asDecl()) {
    auto *ND = cast<NamedDecl>(D);
    S.FFDiag(ND->getLocation(), diag::note_constexpr_destroy_out_of_lifetime)
        << ND->getNameAsString();
  } else {
    S.FFDiag(Ptr.getDeclDesc()->getLocation(),
             diag::note_constexpr_destroy_out_of_lifetime)
        << Ptr.toDiagnosticString(S.getASTContext());
  }
  return false;
}

bool checkDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
  if (!CheckLive(S, OpPC, Ptr, AK_Destroy))
    return false;
  if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Destroy))
    return false;
  if (!CheckRange(S, OpPC, Ptr, AK_Destroy))
    return false;

  if (Ptr.getLifetime() == Lifetime::Destroyed)
    return diagnoseOutOfLifetimeDestroy(S, OpPC, Ptr);
  if (Ptr.getLifetime() == Lifetime::Ended)
    return CheckLifetime(S, OpPC, Ptr, AK_Destroy);

  // We _can_ call the destructor on the global variable we're checking constant
  // destruction for.
  if (S.checkingConstantDestruction(Ptr))
    return true;

  // Can't call a dtor on a global variable.
  if (Ptr.block()->isStatic()) {
    const SourceInfo &E = S.Current->getSource(OpPC);
    S.FFDiag(E, diag::note_constexpr_modify_global);
    return false;
  }
  return CheckActive(S, OpPC, Ptr, AK_Destroy);
}

/// Opcode. Check if the function decl can be called at compile time.
bool CheckFunctionDecl(InterpState &S, CodePtr OpPC, const FunctionDecl *FD) {
  if (S.checkingPotentialConstantExpression() && S.Current->getDepth() != 0)
    return false;

  const FunctionDecl *Definition = nullptr;
  const Stmt *Body = FD->getBody(Definition);

  if (Definition && Body &&
      (Definition->isConstexpr() || (S.Current->MSVCConstexprAllowed &&
                                     Definition->hasAttr<MSConstexprAttr>())))
    return true;

  return diagnoseCallableDecl(S, OpPC, FD);
}

bool CheckBitCast(InterpState &S, CodePtr OpPC, const Type *TargetType,
                  bool SrcIsVoidPtr) {
  const auto &Ptr = S.Stk.peek<Pointer>();
  if (Ptr.isZero())
    return true;
  if (!Ptr.isBlockPointer())
    return true;

  if (TargetType->isIntegerType())
    return true;

  if (SrcIsVoidPtr && S.getLangOpts().CPlusPlus) {
    bool HasValidResult = !Ptr.isZero();

    if (HasValidResult) {
      if (S.getStdAllocatorCaller("allocate"))
        return true;

      const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
      if (S.getLangOpts().CPlusPlus26 &&
          S.getASTContext().hasSimilarType(Ptr.getType(),
                                           QualType(TargetType, 0)))
        return true;

      S.CCEDiag(E, diag::note_constexpr_invalid_void_star_cast)
          << E->getSubExpr()->getType() << S.getLangOpts().CPlusPlus26
          << Ptr.getType().getCanonicalType() << E->getType()->getPointeeType();
    } else if (!S.getLangOpts().CPlusPlus26) {
      const SourceInfo &E = S.Current->getSource(OpPC);
      S.CCEDiag(E, diag::note_constexpr_invalid_cast)
          << diag::ConstexprInvalidCastKind::CastFrom << "'void *'"
          << S.Current->getRange(OpPC);
    }
  }

  QualType PtrType = Ptr.getType();
  if (PtrType->isRecordType() &&
      PtrType->getAsRecordDecl() != TargetType->getAsRecordDecl()) {
    S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
        << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
        << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
  }
  return true;
}

static void compileFunction(InterpState &S, const Function *Func) {
  const FunctionDecl *Definition;
  if (!Func->getDecl()->getBody(Definition))
    return;
  if (!Definition)
    return;

  Compiler<ByteCodeEmitter>(S.getContext(), S.P)
      .compileFunc(Definition, const_cast<Function *>(Func));
}

bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
             uint32_t VarArgSize) {
  if (Func->hasThisPointer()) {
    size_t ArgSize = Func->getArgSize() + VarArgSize;
    size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
    const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);

    // If the current function is a lambda static invoker and
    // the function we're about to call is a lambda call operator,
    // skip the CheckInvoke, since the ThisPtr is a null pointer
    // anyway.
    if (!(S.Current->getFunction() &&
          S.Current->getFunction()->isLambdaStaticInvoker() &&
          Func->isLambdaCallOperator())) {
      if (!CheckInvoke(S, OpPC, ThisPtr, Func->isConstructor(),
                       Func->isDestructor()))
        return false;
    }

    if (S.checkingPotentialConstantExpression())
      return false;
  }

  if (!Func->isFullyCompiled())
    compileFunction(S, Func);

  if (!CheckCallable(S, OpPC, Func))
    return false;

  if (!CheckCallDepth(S, OpPC))
    return false;

  auto Memory = new char[InterpFrame::allocSize(Func)];
  auto NewFrame = new (Memory) InterpFrame(S, Func, S.PC, VarArgSize);
  InterpFrame *FrameBefore = S.Current;
  S.Current = NewFrame;

  InterpStateCCOverride CCOverride(S, Func->isImmediate());
  if (Interpret(S)) {
    assert(S.Current == FrameBefore);
    return true;
  }

  InterpFrame::free(NewFrame);
  // Interpreting the function failed somehow. Reset to
  // previous state.
  S.Current = FrameBefore;
  return false;
}
bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
          uint32_t VarArgSize) {

  // C doesn't have constexpr functions.
  if (!S.getLangOpts().CPlusPlus)
    return Invalid(S, OpPC);

  assert(Func);
  auto cleanup = [&]() -> bool {
    cleanupAfterFunctionCall(S, Func);
    return false;
  };

  bool InstancePtrTracked = false;
  if (Func->hasThisPointer()) {
    size_t ArgSize = Func->getArgSize() + VarArgSize;
    size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);

    const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);

    // C++23 [expr.const]p5.6
    // an invocation of a virtual function ([class.virtual]) for an object whose
    // dynamic type is constexpr-unknown;
    if (ThisPtr.isDummy() && Func->isVirtual())
      return false;

    // If the current function is a lambda static invoker and
    // the function we're about to call is a lambda call operator,
    // skip the CheckInvoke, since the ThisPtr is a null pointer
    // anyway.
    if (S.Current->getFunction() &&
        S.Current->getFunction()->isLambdaStaticInvoker() &&
        Func->isLambdaCallOperator()) {
      assert(ThisPtr.isZero());
    } else {
      if (!CheckInvoke(S, OpPC, ThisPtr, Func->isConstructor(),
                       Func->isDestructor()))
        return cleanup();

      if (Func->isCopyOrMoveOperator() || Func->isCopyOrMoveConstructor()) {
        const Pointer &RVOPtr =
            S.Stk.peek<Pointer>(ThisOffset - align(sizeof(Pointer)));
        if (!CheckInvoke(S, OpPC, RVOPtr, /*IsCtor=*/true, /*IsDtor=*/false))
          return cleanup();
      }

      if (!Func->isConstructor() && !Func->isDestructor() &&
          !CheckActive(S, OpPC, ThisPtr, AK_MemberCall))
        return false;
    }

    if (Func->isConstructor() && !checkConstructor(S, OpPC, Func, ThisPtr))
      return false;
    if (Func->isDestructor() && !checkDestructor(S, OpPC, ThisPtr))
      return false;

    InstancePtrTracked = (Func->isConstructor() || Func->isDestructor());
    if (InstancePtrTracked)
      S.InitializingPtrs.push_back(ThisPtr.view());
  }

  if (!Func->isFullyCompiled())
    compileFunction(S, Func);

  if (!CheckCallable(S, OpPC, Func))
    return cleanup();

  // Do not evaluate any function calls in checkingPotentialConstantExpression
  // mode. Constructors will be aborted later when their initializers are
  // evaluated.
  if (S.checkingPotentialConstantExpression() && !Func->isConstructor())
    return false;

  if (!CheckCallDepth(S, OpPC))
    return cleanup();

  auto Memory = new char[InterpFrame::allocSize(Func)];
  auto NewFrame = new (Memory) InterpFrame(S, Func, S.PC, VarArgSize);
  InterpFrame *FrameBefore = S.Current;
  S.Current = NewFrame;

  InterpStateCCOverride CCOverride(S, Func->isImmediate());
  bool Success = Interpret(S);
  // Remove initializing  block again.
  if (InstancePtrTracked)
    S.InitializingPtrs.pop_back();

  if (!Success) {
    InterpFrame::free(NewFrame);
    // Interpreting the function failed somehow. Reset to
    // previous state.
    S.Current = FrameBefore;
    return false;
  }

  assert(S.Current == FrameBefore);
  return true;
}

static bool getDynamicDecl(InterpState &S, CodePtr OpPC, PtrView TypePtr,
                           const CXXRecordDecl *&DynamicDecl) {

  if (S.InitializingPtrs.empty()) {
    TypePtr = TypePtr.stripBaseCasts();
  } else {
    auto depth = [](PtrView V) -> unsigned {
      unsigned C = 1;
      while (!V.isRoot()) {
        ++C;
        V = V.getBase();
      }
      return C;
    };
    // Consider a 'normal' diamond hierarchy:
    //   A     A   3
    //   |     |
    //   B     C   2
    //    \   /
    //     \ /
    //      D      1
    // When we use a pointer of D*, cast it to B's A* and
    // use it during the construction of C*, the expected
    // dynamic type is B.
    PtrView InitPtr = S.InitializingPtrs.back();
    assert(depth(TypePtr) >= depth(InitPtr));
    unsigned D = depth(TypePtr) - depth(InitPtr);
    for (unsigned I = 0; I != D; ++I)
      TypePtr = TypePtr.getBase();
  }

  QualType DynamicType = TypePtr.getType();
  if (TypePtr.Pointee->isStatic() || TypePtr.isConst()) {
    if (const VarDecl *VD = Pointer(TypePtr).getRootVarDecl();
        VD && !VD->isConstexpr()) {
      const Expr *E = S.Current->getExpr(OpPC);
      APValue V = Pointer(TypePtr).toAPValue(S.getASTContext());
      QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
      S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
          << AK_MemberCall << V.getAsString(S.getASTContext(), TT);
      return false;
    }
  }

  if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
    DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
  } else if (DynamicType->isArrayType()) {
    const Type *ElemType = DynamicType->getPointeeOrArrayElementType();
    assert(ElemType);
    DynamicDecl = ElemType->getAsCXXRecordDecl();
  } else {
    DynamicDecl = DynamicType->getAsCXXRecordDecl();
  }
  return DynamicDecl != nullptr;
}

struct DynamicCastResult {
  UnsignedOrNone Offset = std::nullopt;
  bool Ambiguous = false;

  bool valid() const { return !Ambiguous && Offset; }

  void setOffset(unsigned O) {
    if (!Offset)
      Offset = O;
    else {
      Ambiguous = true;
    }
  }

  void merge(DynamicCastResult C) {
    Ambiguous |= C.Ambiguous;
    if (C.Offset) {
      if (!Offset)
        Offset = C.Offset;
      else
        Ambiguous = true;
    }
  }
};

// Walk UP the type hierarchy, starting at the decl of R to find Needle.
static DynamicCastResult findRecordBase(const ASTContext &Ctx, const Record *R,
                                        QualType Needle) {
  DynamicCastResult Res;

  if (Ctx.hasSimilarType(Needle, Ctx.getCanonicalTagType(R->getDecl())))
    Res.setOffset(0);

  for (const Record::Base &B : R->bases()) {
    auto N = findRecordBase(Ctx, B.R, Needle);
    if (N.Offset)
      N.Offset = *N.Offset + B.Offset;
    Res.merge(N);
  }

  return Res;
}

bool DynamicCast(InterpState &S, CodePtr OpPC, const Type *DestTypePtr,
                 bool IsReferenceCast) {
  const auto &Ptr = S.Stk.pop<Pointer>();
  QualType TargetType = QualType(DestTypePtr, 0);

  if (Ptr.isConstexprUnknown()) {
    QualType T = Ptr.getType();
    const Expr *E = S.Current->getExpr(OpPC);
    APValue V = Ptr.toAPValue(S.getASTContext());
    QualType TT = S.getASTContext().getLValueReferenceType(T);
    S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
        << AK_DynamicCast << V.getAsString(S.getASTContext(), TT);
    return false;
  }

  if (!Ptr.isBlockPointer() || !Ptr.getRecord())
    return false;

  if (!Ptr.isInitialized())
    return DiagnoseUninitialized(S, OpPC, Ptr, AK_Read);

  // Our given pointer, limited by the base that's currently being initialized,
  // if any.
  PtrView LimitedPtr;
  if (S.InitializingPtrs.empty() ||
      S.InitializingPtrs.back().block() != Ptr.block()) {
    LimitedPtr = Ptr.stripBaseCasts().view();
  } else {
    LimitedPtr = S.InitializingPtrs.back();
    assert(LimitedPtr.block() == Ptr.block());
  }
  assert(LimitedPtr.getRecord());

  // C++ [expr.dynamic.cast]p7:
  //   If T is "pointer to cv void", then the result is a pointer to the most
  //   derived object
  if (TargetType->isVoidType()) {
    S.Stk.push<Pointer>(LimitedPtr);
    return true;
  }

  assert(!TargetType.isNull());
  assert(!TargetType->isVoidType());
  assert(TargetType->isRecordType());

  // Helper lambdas.
  auto typesMatch = [&](QualType A, QualType B) -> bool {
    return S.getASTContext().hasSimilarType(A, B);
  };
  auto getRecord = [](PtrView P) -> const CXXRecordDecl * {
    assert(P.getRecord());
    return cast<CXXRecordDecl>(P.getRecord()->getDecl());
  };

  auto baseIsPrivate = [&](PtrView P) -> bool {
    if (P.isRoot() || !P.isBaseClass())
      return false;

    CXXBasePaths Paths;
    getRecord(P.getBase())->isDerivedFrom(getRecord(P), Paths);
    assert(std::distance(Paths.begin(), Paths.end()) == 1);

    return Paths.front().Access == AS_private;
  };

  enum {
    DiagPrivateBase = 0,
    DiagNoBase = 1,
    DiagAmbiguous = 2,
    DiagPrivateSibling = 3
  };

  auto diag = [&](int DiagKind, QualType ResultType) -> bool {
    // Pointer casts return nullptr on failure.
    if (!IsReferenceCast) {
      S.Stk.push<Pointer>(0, DestTypePtr);
      return true;
    }
    QualType DynamicType = LimitedPtr.getType()->getCanonicalTypeUnqualified();
    S.FFDiag(S.Current->getSource(OpPC),
             diag::note_constexpr_dynamic_cast_to_reference_failed)
        << DiagKind << ResultType << DynamicType << TargetType;
    return false;
  };

  // Check if Ptr's dynamic type is derived from our target type at all.
  // If it isn't, diagnose this as "operand does not have base class of type
  // [...]".
  {
    CXXBasePaths Paths;
    getRecord(LimitedPtr)
        ->isDerivedFrom(TargetType->getAsCXXRecordDecl(), Paths);
    if (std::distance(Paths.begin(), Paths.end()) == 0 &&
        !typesMatch(LimitedPtr.getType(), TargetType)) {
      return diag(DiagNoBase, TargetType);
    }
  }

  // Current base is already private.
  if (baseIsPrivate(Ptr.view()))
    return diag(DiagPrivateBase, Ptr.getType());

  std::optional<PtrView> Result;
  // First, check simple downcasts without ambiguities.
  for (PtrView Iter = Ptr.view();;) {
    if (Iter.isRoot() || !Iter.isBaseClass())
      break;

    if (typesMatch(TargetType, Iter.getType())) {
      Result = Iter;
      break;
    }
    // Moving DOWN the type hierarchy.
    Iter = Iter.getBase();
  }

  // Simply walking down the type hierarchy has produced a valid result, use
  // that.
  if (Result) {
    if (baseIsPrivate(*Result))
      return diag(DiagPrivateBase, Result->getType());
    S.Stk.push<Pointer>(*Result);
    return true;
  }

  // Otherwise, we need to do a deep hierarchy check.
  bool Ambiguous = false;
  for (PtrView Iter = LimitedPtr;;) {
    // If we can move up the hierarchy from this level and reach the target type
    // unambiguously, we're fine.
    auto R = findRecordBase(S.getASTContext(), Iter.getRecord(), TargetType);

    if (R.valid()) {
      Result = Iter.atField(*R.Offset);
      break;
    } else if (R.Ambiguous) {
      Ambiguous = true;
      break;
    }

    // This moves us DOWN the type hierarchy.
    Iter = Iter.getBase();
    if (Iter.isRoot() || !Iter.isBaseClass())
      break;
  }

  if (Ambiguous)
    return diag(DiagAmbiguous, TargetType);

  if (Result) {
    // Might still be invalid due to resulting in a private base though.
    if (baseIsPrivate(*Result))
      return diag(DiagPrivateSibling, TargetType);
    S.Stk.push<Pointer>(*Result);
    return true;
  }

  // We couldn't find the requested base.
  return diag(DiagNoBase, TargetType);
}

bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
              uint32_t VarArgSize) {
  assert(Func->hasThisPointer());
  assert(Func->isVirtual());
  size_t ArgSize = Func->getArgSize() + VarArgSize;
  size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
  Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);

  if (!ThisPtr.isBlockPointer())
    return false;

  const FunctionDecl *Callee = Func->getDecl();

  const CXXRecordDecl *DynamicDecl = nullptr;
  if (!getDynamicDecl(S, OpPC, ThisPtr.view(), DynamicDecl))
    return false;
  assert(DynamicDecl);

  const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl());
  const auto *InitialFunction = cast<CXXMethodDecl>(Callee);
  const CXXMethodDecl *Overrider;

  if (StaticDecl != DynamicDecl) {
    if (!DynamicDecl->isDerivedFrom(StaticDecl))
      return false;
    Overrider = S.getContext().getOverridingFunction(DynamicDecl, StaticDecl,
                                                     InitialFunction);

  } else {
    Overrider = InitialFunction;
  }

  // C++2a [class.abstract]p6:
  //   the effect of making a virtual call to a pure virtual function [...] is
  //   undefined
  if (Overrider->isPureVirtual()) {
    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_pure_virtual_call,
             1)
        << Callee;
    S.Note(Callee->getLocation(), diag::note_declared_at);
    return false;
  }

  if (Overrider != InitialFunction) {
    // DR1872: An instantiated virtual constexpr function can't be called in a
    // constant expression (prior to C++20). We can still constant-fold such a
    // call.
    if (!S.getLangOpts().CPlusPlus20 && Overrider->isVirtual()) {
      const Expr *E = S.Current->getExpr(OpPC);
      S.CCEDiag(E, diag::note_constexpr_virtual_call) << E->getSourceRange();
    }

    Func = S.getContext().getOrCreateFunction(Overrider);

    const CXXRecordDecl *ThisFieldDecl =
        ThisPtr.getFieldDesc()->getType()->getAsCXXRecordDecl();
    if (Func->getParentDecl()->isDerivedFrom(ThisFieldDecl)) {
      // If the function we call is further DOWN the hierarchy than the
      // FieldDesc of our pointer, just go up the hierarchy of this field
      // the furthest we can go.
      ThisPtr = ThisPtr.stripBaseCasts();
    }
  }

  if (!Call(S, OpPC, Func, VarArgSize))
    return false;

  // Covariant return types. The return type of Overrider is a pointer
  // or reference to a class type.
  if (Overrider != InitialFunction &&
      Overrider->getReturnType()->isPointerOrReferenceType() &&
      InitialFunction->getReturnType()->isPointerOrReferenceType()) {
    QualType OverriderPointeeType =
        Overrider->getReturnType()->getPointeeType();
    QualType InitialPointeeType =
        InitialFunction->getReturnType()->getPointeeType();

    // Nothing to do if the types already match.
    if (S.getASTContext().hasSimilarType(InitialPointeeType,
                                         OverriderPointeeType))
      return true;

    // We've called Overrider above, but calling code expects us to return what
    // InitialFunction returned. According to the rules for covariant return
    // types, what InitialFunction returns needs to be a base class of what
    // Overrider returns. So, we need to do an upcast here.
    unsigned Offset = S.getContext().collectBaseOffset(
        InitialPointeeType->getAsRecordDecl(),
        OverriderPointeeType->getAsRecordDecl());
    return GetPtrBasePop(S, OpPC, Offset, /*IsNullOK=*/true);
  }

  return true;
}

bool CallBI(InterpState &S, CodePtr OpPC, const CallExpr *CE,
            uint32_t BuiltinID) {
  // A little arbitrary, but the current interpreter allows evaluation
  // of builtin functions in this mode, with some exceptions.
  if (BuiltinID == Builtin::BI__builtin_operator_new &&
      S.checkingPotentialConstantExpression())
    return false;

  return InterpretBuiltin(S, OpPC, CE, BuiltinID);
}

bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
             const CallExpr *CE) {
  const Pointer &Ptr = S.Stk.pop<Pointer>();

  if (Ptr.isZero()) {
    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_null_callee)
        << const_cast<Expr *>(CE->getCallee()) << CE->getSourceRange();
    return false;
  }

  if (!Ptr.isFunctionPointer())
    return Invalid(S, OpPC);

  const Function *F = Ptr.asFunctionPointer().Func;
  assert(F);
  // Don't allow calling block pointers.
  if (!F->getDecl())
    return Invalid(S, OpPC);

  // This happens when the call expression has been cast to
  // something else, but we don't support that.
  if (S.Ctx.classify(F->getDecl()->getReturnType()) !=
      S.Ctx.classify(CE->getCallReturnType(S.getASTContext())))
    return false;

  // Check argument nullability state.
  if (F->hasNonNullAttr()) {
    if (!CheckNonNullArgs(S, OpPC, F, CE, ArgSize))
      return false;
  }

  // Can happen when casting function pointers around.
  QualType CalleeType = CE->getCallee()->getType();
  if (CalleeType->isPointerType() &&
      !S.getASTContext().hasSameFunctionTypeIgnoringExceptionSpec(
          F->getDecl()->getType(), CalleeType->getPointeeType())) {
    return false;
  }

  // We nedd to compile (and check) early for function pointer calls
  // because the Call/CallVirt below might access the instance pointer
  // but the Function's information about them is wrong.
  if (!F->isFullyCompiled())
    compileFunction(S, F);

  if (!CheckCallable(S, OpPC, F))
    return false;

  assert(ArgSize >= F->getWrittenArgSize());
  uint32_t VarArgSize = ArgSize - F->getWrittenArgSize();

  // We need to do this explicitly here since we don't have the necessary
  // information to do it automatically.
  if (F->hasExplicitThisPointer())
    VarArgSize -= align(primSize(PT_Ptr));

  if (F->isVirtual())
    return CallVirt(S, OpPC, F, VarArgSize);

  return Call(S, OpPC, F, VarArgSize);
}

static void startLifetimeRecurse(PtrView Ptr) {
  if (const Record *R = Ptr.getRecord()) {
    Ptr.startLifetime();

    for (const Record::Field &Fi : R->fields()) {
      PtrView FP = Ptr.atField(Fi.Offset);
      if (FP.getLifetime() != Lifetime::Started)
        startLifetimeRecurse(FP);
    }
    return;
  }

  if (const Descriptor *FieldDesc = Ptr.getFieldDesc();
      FieldDesc->isCompositeArray()) {
    for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
      PtrView EP = Ptr.atIndex(I).narrow();
      if (EP.getLifetime() != Lifetime::Started)
        startLifetimeRecurse(EP);
    }
    return;
  }

  Ptr.startLifetime();
}

bool StartThisLifetime(InterpState &S) {
  if (S.checkingPotentialConstantExpression())
    return true;

  const auto &Ptr = S.Current->getThis();
  if (!Ptr.isBlockPointer())
    return false;
  startLifetimeRecurse(Ptr.view());
  return true;
}

bool StartThisLifetime1(InterpState &S) {
  if (S.checkingPotentialConstantExpression())
    return true;

  const auto &Ptr = S.Current->getThis();
  if (!Ptr.isBlockPointer())
    return false;
  Ptr.startLifetime();
  return true;
}

// FIXME: It might be better to the recursing as part of the generated code for
// a destructor?
static void setLifeStateRecurse(PtrView Ptr, Lifetime L) {
  if (const Record *R = Ptr.getRecord()) {
    Ptr.setLifeState(L);
    for (const Record::Field &Fi : R->fields())
      setLifeStateRecurse(Ptr.atField(Fi.Offset), L);
    return;
  }

  if (const Descriptor *FieldDesc = Ptr.getFieldDesc();
      FieldDesc->isCompositeArray()) {
    // No endLifetime() for primitive array roots.
    if (Ptr.getFieldDesc()->isPrimitiveArray())
      assert(Ptr.getLifetime() == Lifetime::Started);
    for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I)
      setLifeStateRecurse(Ptr.atIndex(I).narrow(), L);
    return;
  }

  Ptr.setLifeState(L);
}

/// Ends the lifetime of the peek'd pointer.
bool EndLifetime(InterpState &S, CodePtr OpPC) {
  const auto &Ptr = S.Stk.peek<Pointer>();
  if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
    return false;

  setLifeStateRecurse(Ptr.view().narrow(), Lifetime::Ended);
  return true;
}

/// Ends the lifetime of the pop'd pointer.
bool PseudoDtor(InterpState &S, CodePtr OpPC) {
  const auto &Ptr = S.Stk.pop<Pointer>();
  if (!checkDestructor(S, OpPC, Ptr))
    return false;
  setLifeStateRecurse(Ptr.view().narrow(), Lifetime::Ended);
  return true;
}

bool MarkDestroyed(InterpState &S, CodePtr OpPC) {
  const auto &Ptr = S.Stk.peek<Pointer>();
  if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
    return false;

  setLifeStateRecurse(Ptr.view().narrow(), Lifetime::Destroyed);
  return true;
}

bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
                          std::optional<uint64_t> ArraySize) {
  Pointer &Orig = S.Stk.peek<Pointer>();
  Pointer Ptr = Orig;

  auto directBaseIsUnion = [](const Pointer &Ptr) -> bool {
    if (Ptr.isArrayElement())
      return false;
    const Record *R = Ptr.getBase().getRecord();
    return R && R->isUnion();
  };

  if (Ptr.inUnion() && directBaseIsUnion(Ptr))
    Ptr.activate();

  if (Ptr.isZero()) {
    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_null)
        << AK_Construct;
    return false;
  }

  if (!Ptr.isBlockPointer())
    return false;

  if (!CheckRange(S, OpPC, Ptr, AK_Construct))
    return false;

  startLifetimeRecurse(Ptr.view());

  // Similar to CheckStore(), but with the additional CheckTemporary() call and
  // the AccessKinds are different.
  if (!Ptr.block()->isAccessible()) {
    if (!CheckExtern(S, OpPC, Ptr))
      return false;
    if (!CheckLive(S, OpPC, Ptr, AK_Construct))
      return false;
    return CheckDummy(S, OpPC, Ptr.block(), AK_Construct);
  }
  if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Construct))
    return false;

  // CheckLifetime for this and all base pointers.
  for (PtrView P = Ptr.view();;) {
    if (!CheckLifetime(S, OpPC, P.getLifetime(), P.Pointee, AK_Construct))
      return false;

    if (P.isRoot())
      break;
    P = P.getBase();
  }

  if (!CheckRange(S, OpPC, Ptr, AK_Construct))
    return false;
  if (!CheckGlobal(S, OpPC, Ptr))
    return false;
  if (!CheckConst(S, OpPC, Ptr))
    return false;
  if (!S.inConstantContext() && isConstexprUnknown(Ptr))
    return false;

  if (!InvalidNewDeleteExpr(S, OpPC, E))
    return false;

  const auto *NewExpr = cast<CXXNewExpr>(E);
  const ASTContext &ASTCtx = S.getASTContext();
  QualType StorageType = Ptr.getType();
  QualType AllocType;
  if (ArraySize) {
    AllocType = ASTCtx.getConstantArrayType(
        NewExpr->getAllocatedType(),
        APInt(64, static_cast<uint64_t>(*ArraySize), false), nullptr,
        ArraySizeModifier::Normal, 0);
  } else {
    AllocType = NewExpr->getAllocatedType();
  }

  if (AllocType->isArrayType() && Ptr.isArrayElement() &&
      Ptr.expand().getIndex() == 0) {
    // The destination of placement new is pointing to the first element
    // of an array.  There's a special case in [expr.const]: "[...] if T is an
    // array type, to the first element of such an object [...]".  Handle
    // that case here by using the base of the Pointer.
    QualType AllocElementType =
        ASTCtx.getAsArrayType(AllocType)->getElementType();
    if (ASTCtx.hasSimilarType(AllocElementType, StorageType)) {
      StorageType = Ptr.expand().getArray().getType();
      Orig = Orig.expand();
    }
  }

  if (!ASTCtx.hasSimilarType(AllocType, StorageType)) {
    S.FFDiag(S.Current->getLocation(OpPC),
             diag::note_constexpr_placement_new_wrong_type)
        << StorageType << AllocType;
    return false;
  }

  // Can't activate fields in a union, unless the direct base is the union.
  if (Ptr.inUnion() && !Ptr.isActive() && !directBaseIsUnion(Ptr))
    return CheckActive(S, OpPC, Ptr, AK_Construct);

  return true;
}

bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
  assert(E);

  if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) {
    const FunctionDecl *OperatorNew = NewExpr->getOperatorNew();

    if (NewExpr->getNumPlacementArgs() > 0) {
      // This is allowed pre-C++26, but only an std function or if
      // [[msvc::constexpr]] was used.
      if (S.getLangOpts().CPlusPlus26 || S.Current->isStdFunction() ||
          S.Current->MSVCConstexprAllowed)
        return true;

      S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_placement)
          << /*C++26 feature*/ 1 << E->getSourceRange();
    } else if (
        !OperatorNew
             ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
      S.FFDiag(S.Current->getSource(OpPC),
               diag::note_constexpr_new_non_replaceable)
          << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
      return false;
    } else if (!S.getLangOpts().CPlusPlus26 &&
               NewExpr->getNumPlacementArgs() == 1 &&
               !OperatorNew->isReservedGlobalPlacementOperator()) {
      if (!S.getLangOpts().CPlusPlus26) {
        S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_placement)
            << /*Unsupported*/ 0 << E->getSourceRange();
        return false;
      }
      return true;
    }
  } else {
    const auto *DeleteExpr = cast<CXXDeleteExpr>(E);
    const FunctionDecl *OperatorDelete = DeleteExpr->getOperatorDelete();
    if (!OperatorDelete
             ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
      S.FFDiag(S.Current->getSource(OpPC),
               diag::note_constexpr_new_non_replaceable)
          << isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;
      return false;
    }
  }

  return false;
}

bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
                              const FixedPoint &FP) {
  const Expr *E = S.Current->getExpr(OpPC);
  if (S.checkingForUndefinedBehavior()) {
    S.getASTContext().getDiagnostics().Report(
        E->getExprLoc(), diag::warn_fixedpoint_constant_overflow)
        << FP.toDiagnosticString(S.getASTContext()) << E->getType();
  }
  S.CCEDiag(E, diag::note_constexpr_overflow)
      << FP.toDiagnosticString(S.getASTContext()) << E->getType();
  return S.noteUndefinedBehavior();
}

bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index) {
  const SourceInfo &Loc = S.Current->getSource(OpPC);
  S.FFDiag(Loc,
           diag::err_shufflevector_minus_one_is_undefined_behavior_constexpr)
      << Index;
  return false;
}

bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC,
                                const Pointer &Ptr, unsigned BitWidth) {
  SourceInfo E = S.Current->getSource(OpPC);
  S.CCEDiag(E, diag::note_constexpr_invalid_cast)
      << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);

  if (Ptr.isIntegralPointer())
    return true;

  if (Ptr.isDummy()) {
    if (!CheckIntegralAddressCast(S, OpPC, BitWidth))
      return false;
    return Ptr.getIndex() == 0;
  }

  if (!Ptr.isZero()) {
    // Only allow based lvalue casts if they are lossless.
    if (!CheckIntegralAddressCast(S, OpPC, BitWidth))
      return Invalid(S, OpPC);
  }
  return true;
}

bool CheckIntegralAddressCast(InterpState &S, CodePtr OpPC, unsigned BitWidth) {
  return (S.getASTContext().getTargetInfo().getPointerWidth(LangAS::Default) ==
          BitWidth);
}

bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
  const Pointer &Ptr = S.Stk.pop<Pointer>();

  if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth))
    return false;

  auto Result = S.allocAP<IntegralAP<false>>(BitWidth);
  Result.copy(APInt(BitWidth, Ptr.getIntegerRepresentation()));

  S.Stk.push<IntegralAP<false>>(Result);
  return true;
}

bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
  const Pointer &Ptr = S.Stk.pop<Pointer>();

  if (!CheckPointerToIntegralCast(S, OpPC, Ptr, BitWidth))
    return false;

  auto Result = S.allocAP<IntegralAP<true>>(BitWidth);
  Result.copy(APInt(BitWidth, Ptr.getIntegerRepresentation()));

  S.Stk.push<IntegralAP<true>>(Result);
  return true;
}

bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
                  bool TargetIsUCharOrByte) {
  // This is always fine.
  if (!HasIndeterminateBits)
    return true;

  // Indeterminate bits can only be bitcast to unsigned char or std::byte.
  if (TargetIsUCharOrByte)
    return true;

  const Expr *E = S.Current->getExpr(OpPC);
  QualType ExprType = E->getType();
  S.FFDiag(E, diag::note_constexpr_bit_cast_indet_dest)
      << ExprType << S.getLangOpts().CharIsSigned << E->getSourceRange();
  return false;
}

bool handleReference(InterpState &S, CodePtr OpPC, Block *B) {
  if (isConstexprUnknown(B)) {
    S.Stk.push<Pointer>(B);
    return true;
  }

  const auto &ID = B->getBlockDesc<const InlineDescriptor>();
  if (!ID.IsInitialized) {
    if (!S.checkingPotentialConstantExpression())
      S.FFDiag(S.Current->getSource(OpPC),
               diag::note_constexpr_use_uninit_reference);
    return false;
  }

  assert(B->getDescriptor()->getPrimType() == PT_Ptr);
  S.Stk.push<Pointer>(B->deref<Pointer>());
  return true;
}

bool GetTypeid(InterpState &S, const Type *TypePtr, const Type *TypeInfoType) {
  S.Stk.push<Pointer>(TypePtr, TypeInfoType);
  return true;
}

bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType) {
  const auto &P = S.Stk.pop<Pointer>();

  if (!P.isBlockPointer())
    return false;

  if (P.isConstexprUnknown()) {
    QualType DynamicType = P.getType();
    const Expr *E = S.Current->getExpr(OpPC);
    APValue V = P.toAPValue(S.getASTContext());
    QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
    S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
        << AK_TypeId << V.getAsString(S.getASTContext(), TT);
    return false;
  }

  // Pick the most-derived type.
  CanQualType T = P.stripBaseCasts().getType()->getCanonicalTypeUnqualified();
  // ... unless we're currently constructing this object.
  // FIXME: We have a similar check to this in more places.
  if (S.Current->getFunction()) {
    for (const InterpFrame *Frame = S.Current; Frame; Frame = Frame->Caller) {
      if (const Function *Func = Frame->getFunction();
          Func && (Func->isConstructor() || Func->isDestructor()) &&
          P.block() == Frame->getThis().block()) {
        T = S.getContext().getASTContext().getCanonicalTagType(
            Func->getParentDecl());
        break;
      }
    }
  }

  S.Stk.push<Pointer>(T->getTypePtr(), TypeInfoType);
  return true;
}

bool DiagTypeid(InterpState &S, CodePtr OpPC) {
  const auto *E = cast<CXXTypeidExpr>(S.Current->getExpr(OpPC));
  S.CCEDiag(E, diag::note_constexpr_typeid_polymorphic)
      << E->getExprOperand()->getType()
      << E->getExprOperand()->getSourceRange();
  return false;
}

bool arePotentiallyOverlappingStringLiterals(const Pointer &LHS,
                                             const Pointer &RHS) {
  if (!LHS.pointsToStringLiteral() || !RHS.pointsToStringLiteral())
    return false;

  unsigned LHSOffset = LHS.isOnePastEnd() ? LHS.getNumElems() : LHS.getIndex();
  unsigned RHSOffset = RHS.isOnePastEnd() ? RHS.getNumElems() : RHS.getIndex();
  const auto *LHSLit = cast<StringLiteral>(LHS.getDeclDesc()->asExpr());
  const auto *RHSLit = cast<StringLiteral>(RHS.getDeclDesc()->asExpr());

  StringRef LHSStr(LHSLit->getBytes());
  unsigned LHSLength = LHSStr.size();
  StringRef RHSStr(RHSLit->getBytes());
  unsigned RHSLength = RHSStr.size();

  int32_t IndexDiff = RHSOffset - LHSOffset;
  if (IndexDiff < 0) {
    if (static_cast<int32_t>(LHSLength) < -IndexDiff)
      return false;
    LHSStr = LHSStr.drop_front(-IndexDiff);
  } else {
    if (static_cast<int32_t>(RHSLength) < IndexDiff)
      return false;
    RHSStr = RHSStr.drop_front(IndexDiff);
  }

  unsigned ShorterCharWidth;
  StringRef Shorter;
  StringRef Longer;
  if (LHSLength < RHSLength) {
    ShorterCharWidth = LHS.getFieldDesc()->getElemDataSize();
    Shorter = LHSStr;
    Longer = RHSStr;
  } else {
    ShorterCharWidth = RHS.getFieldDesc()->getElemDataSize();
    Shorter = RHSStr;
    Longer = LHSStr;
  }

  // The null terminator isn't included in the string data, so check for it
  // manually. If the longer string doesn't have a null terminator where the
  // shorter string ends, they aren't potentially overlapping.
  for (unsigned NullByte : llvm::seq(ShorterCharWidth)) {
    if (Shorter.size() + NullByte >= Longer.size())
      break;
    if (Longer[Shorter.size() + NullByte])
      return false;
  }
  return Shorter == Longer.take_front(Shorter.size());
}

static void copyPrimitiveMemory(InterpState &S, const Pointer &Ptr,
                                PrimType T) {
  if (T == PT_IntAPS) {
    auto &Val = Ptr.deref<IntegralAP<true>>();
    if (!Val.singleWord()) {
      uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
      Val.take(NewMemory);
    }
  } else if (T == PT_IntAP) {
    auto &Val = Ptr.deref<IntegralAP<false>>();
    if (!Val.singleWord()) {
      uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
      Val.take(NewMemory);
    }
  } else if (T == PT_Float) {
    auto &Val = Ptr.deref<Floating>();
    if (!Val.singleWord()) {
      uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
      Val.take(NewMemory);
    }
  } else if (T == PT_MemberPtr) {
    auto &Val = Ptr.deref<MemberPointer>();
    unsigned PathLength = Val.getPathLength();
    auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
    std::copy_n(Val.path(), PathLength, NewPath);
    Val.takePath(NewPath);
  }
}

template <typename T>
static void copyPrimitiveMemory(InterpState &S, const Pointer &Ptr) {
  assert(needsAlloc<T>());
  if constexpr (std::is_same_v<T, MemberPointer>) {
    auto &Val = Ptr.deref<MemberPointer>();
    unsigned PathLength = Val.getPathLength();
    auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
    std::copy_n(Val.path(), PathLength, NewPath);
    Val.takePath(NewPath);
  } else {
    auto &Val = Ptr.deref<T>();
    if (!Val.singleWord()) {
      uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
      Val.take(NewMemory);
    }
  }
}

static void finishGlobalRecurse(InterpState &S, const Pointer &Ptr) {
  if (const Record *R = Ptr.getRecord()) {
    for (const Record::Field &Fi : R->fields()) {
      if (Fi.Desc->isPrimitive()) {
        TYPE_SWITCH_ALLOC(Fi.Desc->getPrimType(), {
          copyPrimitiveMemory<T>(S, Ptr.atField(Fi.Offset));
        });
      } else {
        finishGlobalRecurse(S, Ptr.atField(Fi.Offset));
      }
    }
    return;
  }

  if (const Descriptor *D = Ptr.getFieldDesc(); D && D->isArray()) {
    unsigned NumElems = D->getNumElems();
    if (NumElems == 0)
      return;

    if (D->isPrimitiveArray()) {
      PrimType PT = D->getPrimType();
      if (!needsAlloc(PT))
        return;
      assert(NumElems >= 1);
      const Pointer EP = Ptr.atIndex(0);
      bool AllSingleWord = true;
      TYPE_SWITCH_ALLOC(PT, {
        if (!EP.deref<T>().singleWord()) {
          copyPrimitiveMemory<T>(S, EP);
          AllSingleWord = false;
        }
      });
      if (AllSingleWord)
        return;
      for (unsigned I = 1; I != D->getNumElems(); ++I) {
        const Pointer EP = Ptr.atIndex(I);
        copyPrimitiveMemory(S, EP, PT);
      }
    } else {
      assert(D->isCompositeArray());
      for (unsigned I = 0; I != D->getNumElems(); ++I) {
        const Pointer EP = Ptr.atIndex(I).narrow();
        finishGlobalRecurse(S, EP);
      }
    }
  }
}

bool FinishInitGlobal(InterpState &S) {
  const Pointer &Ptr = S.Stk.pop<Pointer>();

  finishGlobalRecurse(S, Ptr);
  if (Ptr.canBeInitialized()) {
    Ptr.initialize();
    Ptr.activate();
  }

  return true;
}

bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind, bool Fatal) {
  const SourceLocation &Loc = S.Current->getLocation(OpPC);

  switch (Kind) {
  case CastKind::Reinterpret:
    S.CCEDiag(Loc, diag::note_constexpr_invalid_cast)
        << diag::ConstexprInvalidCastKind::Reinterpret
        << S.Current->getRange(OpPC);
    return !Fatal;
  case CastKind::ReinterpretLike:
    S.CCEDiag(Loc, diag::note_constexpr_invalid_cast)
        << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
        << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
    return !Fatal;
  case CastKind::Volatile:
    if (!S.checkingPotentialConstantExpression()) {
      const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
      if (S.getLangOpts().CPlusPlus)
        S.FFDiag(E, diag::note_constexpr_access_volatile_type)
            << AK_Read << E->getSubExpr()->getType();
      else
        S.FFDiag(E);
    }

    return false;
  case CastKind::Dynamic:
    assert(!S.getLangOpts().CPlusPlus20);
    S.CCEDiag(Loc, diag::note_constexpr_invalid_cast)
        << diag::ConstexprInvalidCastKind::Dynamic;
    return true;
  }
  llvm_unreachable("Unhandled CastKind");
  return false;
}

bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) {
  assert(S.Current->getFunction());
  // FIXME: We iterate the scope once here and then again in the destroy() call
  // below.
  for (auto &Local : S.Current->getFunction()->getScope(I).locals_reverse()) {
    if (!S.Current->getLocalBlock(Local.Offset)->isInitialized())
      continue;
    const Pointer &Ptr = S.Current->getLocalPointer(Local.Offset);
    if (Ptr.getLifetime() == Lifetime::Ended)
      return diagnoseOutOfLifetimeDestroy(S, OpPC, Ptr);
  }

  S.Current->destroy(I);
  return true;
}

// Perform a cast towards the class of the Decl (either up or down the
// hierarchy).
static bool castBackMemberPointer(InterpState &S,
                                  const MemberPointer &MemberPtr,
                                  int32_t BaseOffset,
                                  const RecordDecl *BaseDecl) {
  const CXXRecordDecl *Expected;
  if (MemberPtr.getPathLength() >= 2)
    Expected = MemberPtr.getPathEntry(MemberPtr.getPathLength() - 2);
  else
    Expected = MemberPtr.getRecordDecl();

  assert(Expected);
  if (Expected->getCanonicalDecl() != BaseDecl->getCanonicalDecl()) {
    // C++11 [expr.static.cast]p12: In a conversion from (D::*) to (B::*),
    // if B does not contain the original member and is not a base or
    // derived class of the class containing the original member, the result
    // of the cast is undefined.
    // C++11 [conv.mem]p2 does not cover this case for a cast from (B::*) to
    // (D::*). We consider that to be a language defect.
    return false;
  }

  unsigned OldPathLength = MemberPtr.getPathLength();
  unsigned NewPathLength = OldPathLength - 1;
  bool IsDerivedMember = NewPathLength != 0;
  auto NewPath = S.allocMemberPointerPath(NewPathLength);
  std::copy_n(MemberPtr.path(), NewPathLength, NewPath);

  S.Stk.push<MemberPointer>(MemberPtr.atInstanceBase(BaseOffset, NewPathLength,
                                                     NewPath, IsDerivedMember));
  return true;
}

static bool appendToMemberPointer(InterpState &S,
                                  const MemberPointer &MemberPtr,
                                  int32_t BaseOffset,
                                  const RecordDecl *BaseDecl,
                                  bool IsDerivedMember) {
  unsigned OldPathLength = MemberPtr.getPathLength();
  unsigned NewPathLength = OldPathLength + 1;

  auto NewPath = S.allocMemberPointerPath(NewPathLength);
  std::copy_n(MemberPtr.path(), OldPathLength, NewPath);
  NewPath[OldPathLength] = cast<CXXRecordDecl>(BaseDecl);

  S.Stk.push<MemberPointer>(MemberPtr.atInstanceBase(BaseOffset, NewPathLength,
                                                     NewPath, IsDerivedMember));
  return true;
}

/// DerivedToBaseMemberPointer
bool CastMemberPtrBasePop(InterpState &S, int32_t Off,
                          const RecordDecl *BaseDecl) {
  const auto &Ptr = S.Stk.pop<MemberPointer>();

  if (!Ptr.isDerivedMember() && Ptr.hasPath())
    return castBackMemberPointer(S, Ptr, Off, BaseDecl);

  bool IsDerivedMember = Ptr.isDerivedMember() || !Ptr.hasPath();
  return appendToMemberPointer(S, Ptr, Off, BaseDecl, IsDerivedMember);
}

/// BaseToDerivedMemberPointer
bool CastMemberPtrDerivedPop(InterpState &S, int32_t Off,
                             const RecordDecl *BaseDecl) {
  const auto &Ptr = S.Stk.pop<MemberPointer>();

  if (!Ptr.isDerivedMember()) {
    // Simply append.
    return appendToMemberPointer(S, Ptr, Off, BaseDecl,
                                 /*IsDerivedMember=*/false);
  }

  return castBackMemberPointer(S, Ptr, Off, BaseDecl);
}

bool GetMemberPtr(InterpState &S, const ValueDecl *D) {
  S.Stk.push<MemberPointer>(D);
  return true;
}

bool GetMemberPtrBase(InterpState &S) {
  const auto &MP = S.Stk.pop<MemberPointer>();

  if (!MP.isBaseCastPossible())
    return false;

  S.Stk.push<Pointer>(MP.getBase());
  return true;
}

bool GetMemberPtrDecl(InterpState &S) {
  const auto &MP = S.Stk.pop<MemberPointer>();

  const ValueDecl *D = MP.getDecl();
  const auto *FD = dyn_cast_if_present<FunctionDecl>(D);
  if (!FD)
    return false;

  const auto *Method = dyn_cast<CXXMethodDecl>(FD);
  if (!Method)
    return false;

  const Pointer &Base = MP.getBase();
  // The method must be accessible via the base of the MemberPointer.
  const CXXRecordDecl *MethodParent = Method->getParent();
  if (!Base.getRecord() || Base.getRecord()->getDecl() != MethodParent)
    return false;

  const auto *Func = S.getContext().getOrCreateFunction(FD);
  if (!Func)
    return false;
  S.Stk.push<Pointer>(Func);
  return true;
}

/// Just append the given Entry to the MemberPointer's path.
/// This is used to re-inject APValues into the bytecode interpreter.
bool CopyMemberPtrPath(InterpState &S, const RecordDecl *Entry,
                       bool IsDerived) {
  const auto &MemberPtr = S.Stk.pop<MemberPointer>();

  unsigned OldPathLength = MemberPtr.getPathLength();
  unsigned NewPathLength = OldPathLength + 1;

  auto NewPath = S.allocMemberPointerPath(NewPathLength);
  std::copy_n(MemberPtr.path(), OldPathLength, NewPath);
  NewPath[OldPathLength] = cast<CXXRecordDecl>(Entry);

  S.Stk.push<MemberPointer>(
      MemberPtr.withPath(NewPathLength, NewPath, IsDerived));
  return true;
}

template <bool Signed>
static bool floatAPCast(InterpState &S, CodePtr OpPC, const Floating &F,
                        uint32_t BitWidth, uint32_t FPOI) {
  APSInt Result(BitWidth, /*IsUnsigned=*/!Signed);
  auto Status = F.convertToInteger(Result);

  // Float-to-Integral overflow check.
  if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite() &&
      !handleOverflow(S, OpPC, F.getAPFloat()))
    return false;

  FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);

  auto ResultAP = S.allocAP<IntegralAP<Signed>>(BitWidth);
  ResultAP.copy(Result);

  S.Stk.push<IntegralAP<Signed>>(ResultAP);

  return CheckFloatResult(S, OpPC, F, Status, FPO);
}

bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth,
                            uint32_t FPOI) {
  Floating F = S.Stk.pop<Floating>();
  return floatAPCast<false>(S, OpPC, F, BitWidth, FPOI);
}

bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth,
                             uint32_t FPOI) {
  Floating F = S.Stk.pop<Floating>();
  return floatAPCast<true>(S, OpPC, F, BitWidth, FPOI);
}

// FIXME: Would be nice to generate this instead of hardcoding it here.
constexpr bool OpReturns(Opcode Op) {
  return Op == OP_RetVoid || Op == OP_RetValue || Op == OP_NoRet ||
         Op == OP_RetSint8 || Op == OP_RetUint8 || Op == OP_RetSint16 ||
         Op == OP_RetUint16 || Op == OP_RetSint32 || Op == OP_RetUint32 ||
         Op == OP_RetSint64 || Op == OP_RetUint64 || Op == OP_RetIntAP ||
         Op == OP_RetIntAPS || Op == OP_RetBool || Op == OP_RetFixedPoint ||
         Op == OP_RetPtr || Op == OP_RetMemberPtr || Op == OP_RetFloat ||
         Op == OP_EndSpeculation;
}

#if USE_TAILCALLS
PRESERVE_NONE static bool InterpNext(InterpState &S);
#endif

// The dispatcher functions read the opcode arguments from the
// bytecode and call the implementation function.
#define GET_INTERPFN_DISPATCHERS
#include "Opcodes.inc"
#undef GET_INTERPFN_DISPATCHERS

using InterpFn = bool (*)(InterpState &) PRESERVE_NONE;
// Array of the dispatcher functions defined above.
const InterpFn InterpFunctions[] = {
#define GET_INTERPFN_LIST
#include "Opcodes.inc"
#undef GET_INTERPFN_LIST
};

#if USE_TAILCALLS
// Read the next opcode and call the dispatcher function.
PRESERVE_NONE static bool InterpNext(InterpState &S) {
  auto Op = S.PC.read<Opcode>();
  auto Fn = InterpFunctions[Op];
  MUSTTAIL return Fn(S);
}
#endif

bool Interpret(InterpState &S) {
  // The current stack frame when we started Interpret().
  // This is being used by the ops to determine wheter
  // to return from this function and thus terminate
  // interpretation.
  assert(!S.Current->isRoot());

  S.PC = S.Current->getFunction()->getCodeBegin();

#if USE_TAILCALLS
  return InterpNext(S);
#else
  while (true) {
    auto Op = S.PC.read<Opcode>();
    auto Fn = InterpFunctions[Op];

    if (!Fn(S))
      return false;
    if (OpReturns(Op))
      break;
  }
  return true;
#endif
}

/// This is used to implement speculative execution via __builtin_constant_p
/// when we generate bytecode.
///
/// The setup here is that we use the same tailcall mechanism for speculative
/// evaluation that we use for the regular one.
/// Since each speculative execution ends with an EndSpeculation opcode,
/// that one does NOT call InterpNext() but simply returns true.
/// This way, we return back to this function when we see an EndSpeculation,
/// OR (of course), when we encounter an error and one of the opcodes
/// returns false.
PRESERVE_NONE static bool BCP(InterpState &S, CodePtr OpPC, int32_t Offset,
                              PrimType PT) {
  // PC after reading the BCP opcode and both Offset/PT arguments.
  [[maybe_unused]] CodePtr PCBefore = S.PC;
  size_t StackSizeBefore = S.Stk.size();

  // Speculation depth must be at least 1 here, since we must have
  // passed a StartSpeculation op before.
#ifndef NDEBUG
  [[maybe_unused]] unsigned DepthBefore = S.SpeculationDepth;
  assert(DepthBefore >= 1);
#endif

  auto SpeculativeInterp = [&S]() -> bool {
    // Ignore diagnostics during speculative execution.
    PushIgnoreDiags(S);
    auto _ = llvm::scope_exit([&]() { PopIgnoreDiags(S); });

#if USE_TAILCALLS
    auto Op = S.PC.read<Opcode>();
    auto Fn = InterpFunctions[Op];
    return Fn(S);
#else
    while (true) {
      auto Op = S.PC.read<Opcode>();
      auto Fn = InterpFunctions[Op];

      if (!Fn(S))
        return false;
      if (OpReturns(Op))
        break;
    }
    return true;
#endif
  };

  if (SpeculativeInterp()) {
    // Speculation must've ended naturally via a EndSpeculation opcode.
    assert(S.SpeculationDepth == DepthBefore - 1);
    if (PT == PT_Ptr) {
      const auto &Ptr = S.Stk.pop<Pointer>();
      assert(S.Stk.size() == StackSizeBefore);
      S.Stk.push<Integral<32, true>>(
          Integral<32, true>::from(CheckBCPResult(S, Ptr)));
    } else {
      // Pop the result from the stack and return success.
      TYPE_SWITCH(PT, S.Stk.discard<T>(););
      assert(S.Stk.size() == StackSizeBefore);
      S.Stk.push<Integral<32, true>>(Integral<32, true>::from(1));
    }
  } else {
    // Jump to the end of the speculation, just after the actual EndSpeculation
    // op.
    S.PC = PCBefore + Offset - align(sizeof(Opcode));

    // End the speculation manually since we didn't call EndSpeculation
    // naturally.
    EndSpeculation(S);

    if (!S.inConstantContext())
      return Invalid(S, OpPC);

    S.Stk.clearTo(StackSizeBefore);
    S.Stk.push<Integral<32, true>>(Integral<32, true>::from(0));
  }

  // We have already evaluated this speculation's EndSpeculation opcode.
  assert(S.SpeculationDepth == DepthBefore - 1);

  return true;
}

} // namespace interp
} // namespace clang
