Unverified Commit 1fee6a1e authored by Timm Baeder's avatar Timm Baeder Committed by GitHub
Browse files

[clang][bytecode] Ignore GetPtrDerivedPop on non-record pointers (#194005)

Now that we do this for `GetPtrBase`, we need to do it here, too. This
broke in the attached test case, but fully fixing it requires some
seemingly unrelated changes to `delete` handling.
parent 166c2418
Loading
Loading
Loading
Loading
+42 −3
Original line number Diff line number Diff line
@@ -1300,7 +1300,7 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,

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

    Source = Ptr.getDeclDesc()->asExpr();
    BlockToDelete = Ptr.block();
@@ -1515,11 +1515,50 @@ static bool getBase(InterpState &S, CodePtr OpPC, const Pointer &Ptr,

bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
  const auto &Ptr = S.Stk.peek<Pointer>();
  return getBase(S, OpPC, Ptr, Off, /*NullOK=*/true);
  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, Off, NullOK);
  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 (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
    return false;
  if (!CheckDowncast(S, OpPC, Ptr, Off))
    return false;

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

  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,
+3 −34
Original line number Diff line number Diff line
@@ -1985,6 +1985,9 @@ bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off);
bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off);
bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK);

bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK,
                      const Type *TargetType);

inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
  if (S.checkingPotentialConstantExpression() && S.Current->getDepth() == 0)
    return false;
@@ -1995,40 +1998,6 @@ inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
  return true;
}

inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off,
                             bool NullOK, const Type *TargetType) {
  const Pointer &Ptr = S.Stk.pop<Pointer>();
  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 (!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;
}

inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
  if (S.checkingPotentialConstantExpression() && S.Current->isBottomFrame())
    return false;
+34 −0
Original line number Diff line number Diff line
@@ -1154,6 +1154,40 @@ namespace BrokenDelete {
  }
}

namespace vdtor {
  constexpr int vdtor_3(int mode) {
    int a = 0;
    struct S { constexpr virtual ~S() {} };
    struct T : S {
      constexpr T(int *p) : p(p) {}
      constexpr ~T() { ++*p; }
      int *p;
    };
    S *p = new T[3]{&a, &a, &a}; // both-note 2{{heap allocation}} \
                                 // both-note {{allocated with 'new[]' here}}
    switch (mode) {
    case 0:
      delete p; // both-note {{non-array delete used to delete pointer to array object of type 'T[3]'}} \
                // both-warning {{'delete' applied to a pointer that was allocated with 'new[]'}}
      break;
    case 1:
      delete[] p; // both-note {{delete of pointer to subobject '&{*new T[3]#0}[0]'}}
      break;
    case 2:
      delete (T*)p; // both-note {{non-array delete used to delete pointer to array object of type 'T[3]'}}
      break;
    case 3:
      delete[] (T*)p;
      break;
    }
    return a;
  }
  static_assert(vdtor_3(0) == 3); // both-error {{}} both-note {{in call}}
  static_assert(vdtor_3(1) == 1); // both-error {{}} both-note {{in call}}
  static_assert(vdtor_3(2) == 3); // both-error {{}} both-note {{in call}}
  static_assert(vdtor_3(3) == 3);
}

#else
/// Make sure we reject this prior to C++20
constexpr int a() { // both-error {{never produces a constant expression}}