has a constexpr destructor. For constexpr variables, reject if the variable does not have constant destruction. In all cases, do not emit runtime calls to the destructor for variables with constant destruction. llvm-svn: 373159
418 lines
12 KiB
C++
418 lines
12 KiB
C++
//===--- InterpState.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 <limits>
|
|
#include <vector>
|
|
#include "Function.h"
|
|
#include "InterpFrame.h"
|
|
#include "InterpStack.h"
|
|
#include "Opcode.h"
|
|
#include "PrimType.h"
|
|
#include "Program.h"
|
|
#include "State.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/ASTDiagnostic.h"
|
|
#include "clang/AST/CXXInheritance.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "llvm/ADT/APSInt.h"
|
|
|
|
using namespace clang;
|
|
using namespace clang::interp;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Ret
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
static bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
|
|
S.CallStackDepth--;
|
|
const T &Ret = S.Stk.pop<T>();
|
|
|
|
assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
|
|
if (!S.checkingPotentialConstantExpression())
|
|
S.Current->popArgs();
|
|
|
|
if (InterpFrame *Caller = S.Current->Caller) {
|
|
PC = S.Current->getRetPC();
|
|
delete S.Current;
|
|
S.Current = Caller;
|
|
S.Stk.push<T>(Ret);
|
|
} else {
|
|
delete S.Current;
|
|
S.Current = nullptr;
|
|
if (!ReturnValue<T>(Ret, Result))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
|
|
S.CallStackDepth--;
|
|
|
|
assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
|
|
if (!S.checkingPotentialConstantExpression())
|
|
S.Current->popArgs();
|
|
|
|
if (InterpFrame *Caller = S.Current->Caller) {
|
|
PC = S.Current->getRetPC();
|
|
delete S.Current;
|
|
S.Current = Caller;
|
|
} else {
|
|
delete S.Current;
|
|
S.Current = nullptr;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool RetValue(InterpState &S, CodePtr &Pt, APValue &Result) {
|
|
llvm::report_fatal_error("Interpreter cannot return values");
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Jmp, Jt, Jf
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) {
|
|
PC += Offset;
|
|
return true;
|
|
}
|
|
|
|
static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) {
|
|
if (S.Stk.pop<bool>()) {
|
|
PC += Offset;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
|
|
if (!S.Stk.pop<bool>()) {
|
|
PC += Offset;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
|
AccessKinds AK) {
|
|
if (Ptr.isInitialized())
|
|
return true;
|
|
if (!S.checkingPotentialConstantExpression()) {
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
S.FFDiag(Loc, diag::note_constexpr_access_uninit) << AK << false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
|
AccessKinds AK) {
|
|
if (Ptr.isActive())
|
|
return true;
|
|
|
|
// Get the inactive field descriptor.
|
|
const FieldDecl *InactiveField = Ptr.getField();
|
|
|
|
// Walk up the pointer chain to find the union which is not active.
|
|
Pointer U = Ptr.getBase();
|
|
while (!U.isActive()) {
|
|
U = U.getBase();
|
|
}
|
|
|
|
// Find the active field of the union.
|
|
Record *R = U.getRecord();
|
|
assert(R && R->isUnion() && "Not a union");
|
|
const FieldDecl *ActiveField = nullptr;
|
|
for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
|
|
const Pointer &Field = U.atField(R->getField(I)->Offset);
|
|
if (Field.isActive()) {
|
|
ActiveField = Field.getField();
|
|
break;
|
|
}
|
|
}
|
|
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
S.FFDiag(Loc, diag::note_constexpr_access_inactive_union_member)
|
|
<< AK << InactiveField << !ActiveField << ActiveField;
|
|
return false;
|
|
}
|
|
|
|
static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
|
AccessKinds AK) {
|
|
if (auto ID = Ptr.getDeclID()) {
|
|
if (!Ptr.isStaticTemporary())
|
|
return true;
|
|
|
|
if (Ptr.getDeclDesc()->getType().isConstQualified())
|
|
return true;
|
|
|
|
if (S.P.getCurrentDecl() == ID)
|
|
return true;
|
|
|
|
const SourceInfo &E = S.Current->getSource(OpPC);
|
|
S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
|
|
S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
|
|
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 {
|
|
|
|
bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
|
|
if (!Ptr.isExtern())
|
|
return true;
|
|
|
|
if (!S.checkingPotentialConstantExpression()) {
|
|
auto *VD = Ptr.getDeclDesc()->asValueDecl();
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
S.FFDiag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
|
|
S.Note(VD->getLocation(), diag::note_declared_at);
|
|
}
|
|
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) {
|
|
const auto &Src = S.Current->getSource(OpPC);
|
|
if (Ptr.isZero()) {
|
|
|
|
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()) {
|
|
bool IsTemp = Ptr.isTemporary();
|
|
|
|
S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
|
|
|
|
if (IsTemp)
|
|
S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
|
|
else
|
|
S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
return false;
|
|
}
|
|
|
|
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
|
AccessKinds AK) {
|
|
if (!Ptr.isOnePastEnd())
|
|
return true;
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
S.FFDiag(Loc, diag::note_constexpr_access_past_end) << AK;
|
|
return false;
|
|
}
|
|
|
|
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
|
CheckSubobjectKind CSK) {
|
|
if (!Ptr.isElementPastEnd())
|
|
return true;
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK;
|
|
return false;
|
|
}
|
|
|
|
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
|
|
assert(Ptr.isLive() && "Pointer is not live");
|
|
if (!Ptr.isConst()) {
|
|
return true;
|
|
}
|
|
|
|
const QualType Ty = Ptr.getType();
|
|
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, const Pointer &Ptr) {
|
|
assert(Ptr.isLive() && "Pointer is not live");
|
|
if (!Ptr.isMutable()) {
|
|
return true;
|
|
}
|
|
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
const FieldDecl *Field = Ptr.getField();
|
|
S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field;
|
|
S.Note(Field->getLocation(), diag::note_declared_at);
|
|
return false;
|
|
}
|
|
|
|
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
|
|
if (!CheckLive(S, OpPC, Ptr, AK_Read))
|
|
return false;
|
|
if (!CheckExtern(S, OpPC, Ptr))
|
|
return false;
|
|
if (!CheckRange(S, OpPC, Ptr, AK_Read))
|
|
return false;
|
|
if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
|
|
return false;
|
|
if (!CheckActive(S, OpPC, Ptr, AK_Read))
|
|
return false;
|
|
if (!CheckTemporary(S, OpPC, Ptr, AK_Read))
|
|
return false;
|
|
if (!CheckMutable(S, OpPC, Ptr))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
|
|
if (!CheckLive(S, OpPC, Ptr, AK_Assign))
|
|
return false;
|
|
if (!CheckExtern(S, OpPC, Ptr))
|
|
return false;
|
|
if (!CheckRange(S, OpPC, Ptr, AK_Assign))
|
|
return false;
|
|
if (!CheckGlobal(S, OpPC, Ptr))
|
|
return false;
|
|
if (!CheckConst(S, OpPC, Ptr))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
|
|
if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
|
|
return false;
|
|
if (!CheckExtern(S, OpPC, Ptr))
|
|
return false;
|
|
if (!CheckRange(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;
|
|
}
|
|
|
|
bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) {
|
|
const SourceLocation &Loc = S.Current->getLocation(OpPC);
|
|
|
|
if (F->isVirtual()) {
|
|
if (!S.getLangOpts().CPlusPlus2a) {
|
|
S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!F->isConstexpr()) {
|
|
if (S.getLangOpts().CPlusPlus11) {
|
|
const FunctionDecl *DiagDecl = F->getDecl();
|
|
|
|
// If this function is not constexpr because it is an inherited
|
|
// non-constexpr constructor, diagnose that directly.
|
|
auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
|
|
if (CD && CD->isInheritingConstructor()) {
|
|
auto *Inherited = CD->getInheritedConstructor().getConstructor();
|
|
if (!Inherited->isConstexpr())
|
|
DiagDecl = CD = Inherited;
|
|
}
|
|
|
|
// 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(Loc, diag::note_constexpr_invalid_inhctor, 1)
|
|
<< CD->getInheritedConstructor().getConstructor()->getParent();
|
|
else
|
|
S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
|
|
<< DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
|
|
S.Note(DiagDecl->getLocation(), diag::note_declared_at);
|
|
} else {
|
|
S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) {
|
|
if (!This.isZero())
|
|
return true;
|
|
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
|
|
bool IsImplicit = false;
|
|
if (auto *E = dyn_cast_or_null<CXXThisExpr>(Loc.asExpr()))
|
|
IsImplicit = E->isImplicit();
|
|
|
|
if (S.getLangOpts().CPlusPlus11)
|
|
S.FFDiag(Loc, diag::note_constexpr_this) << IsImplicit;
|
|
else
|
|
S.FFDiag(Loc);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
|
|
if (!MD->isPure())
|
|
return true;
|
|
const SourceInfo &E = S.Current->getSource(OpPC);
|
|
S.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << MD;
|
|
S.Note(MD->getLocation(), diag::note_declared_at);
|
|
return false;
|
|
}
|
|
bool Interpret(InterpState &S, APValue &Result) {
|
|
CodePtr PC = S.Current->getPC();
|
|
|
|
for (;;) {
|
|
auto Op = PC.read<Opcode>();
|
|
CodePtr OpPC = PC;
|
|
|
|
switch (Op) {
|
|
#define GET_INTERP
|
|
#include "Opcodes.inc"
|
|
#undef GET_INTERP
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace interp
|
|
} // namespace clang
|