Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3235,7 +3235,8 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
return this->visitInitializer(E->getArg(0));

// Zero initialization.
if (E->requiresZeroInitialization()) {
bool ZeroInit = E->requiresZeroInitialization();
if (ZeroInit) {
const Record *R = getRecord(E->getType());

if (!this->visitZeroRecordInitializer(R, E))
Expand All @@ -3246,6 +3247,19 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
return true;
}

// Avoid materializing a temporary for an elidable copy/move constructor.
if (!ZeroInit && E->isElidable()) {
const Expr *SrcObj = E->getArg(0);
assert(SrcObj->isTemporaryObject(Ctx.getASTContext(), Ctor->getParent()));
assert(Ctx.getASTContext().hasSameUnqualifiedType(E->getType(),
SrcObj->getType()));
if (const auto *ME = dyn_cast<MaterializeTemporaryExpr>(SrcObj)) {
if (!this->emitCheckFunctionDecl(Ctor, E))
return false;
return this->visitInitializer(ME->getSubExpr());
}
}

const Function *Func = getFunction(Ctor);

if (!Func)
Expand Down
80 changes: 49 additions & 31 deletions clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -919,45 +919,19 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return true;
}

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 (S.checkingPotentialConstantExpression() && S.Current->getDepth() != 0)
return false;

if (F->isValid() && F->hasBody() && F->isConstexpr())
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;

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() &&
F->getDecl()->getIdentifier()) {
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 = F->getDecl()->getName();
StringRef Name = DiagDecl->getName();
bool AssertFailed =
Name == "__assert_rtn" || Name == "__assert_fail" || Name == "_wassert";
if (AssertFailed) {
Expand Down Expand Up @@ -1004,7 +978,7 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
// for a constant expression. It might be defined at the point we're
// actually calling it.
bool IsExtern = DiagDecl->getStorageClass() == SC_Extern;
bool IsDefined = F->isDefined();
bool IsDefined = DiagDecl->isDefined();
if (!IsDefined && !IsExtern && DiagDecl->isConstexpr() &&
S.checkingPotentialConstantExpression())
return false;
Expand All @@ -1027,6 +1001,35 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
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 (S.checkingPotentialConstantExpression() && S.Current->getDepth() != 0)
return false;

if (F->isValid() && F->hasBody() && F->isConstexpr())
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),
Expand Down Expand Up @@ -1500,6 +1503,21 @@ bool CheckDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
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() || Definition->hasAttr<MSConstexprAttr>()))
return true;

return diagnoseCallableDecl(S, OpPC, FD);
}

static void compileFunction(InterpState &S, const Function *Func) {
const FunctionDecl *Definition = Func->getDecl()->getDefinition();
if (!Definition)
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
bool TargetIsUCharOrByte);
bool CheckBCPResult(InterpState &S, const Pointer &Ptr);
bool CheckDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
bool CheckFunctionDecl(InterpState &S, CodePtr OpPC, const FunctionDecl *FD);

bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
const FixedPoint &FP);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/ByteCode/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def ArgBool : ArgType { let Name = "bool"; }
def ArgFixedPoint : ArgType { let Name = "FixedPoint"; let AsRef = true; }

def ArgFunction : ArgType { let Name = "const Function *"; }
def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; }
def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; }
def ArgRecordField : ArgType { let Name = "const Record::Field *"; }
def ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; }
Expand Down Expand Up @@ -422,6 +423,8 @@ def CheckLiteralType : Opcode {

def CheckArraySize : Opcode { let Args = [ArgUint64]; }

def CheckFunctionDecl : Opcode { let Args = [ArgFunctionDecl]; }

// [] -> [Value]
def GetGlobal : AccessOpcode;
def GetGlobalUnchecked : AccessOpcode;
Expand Down
8 changes: 8 additions & 0 deletions clang/test/AST/ByteCode/cxx11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,11 @@ namespace DiscardedAddrLabel {
}
}

struct Counter {
int copies;
constexpr Counter(int copies) : copies(copies) {}
constexpr Counter(const Counter& other) : copies(other.copies + 1) {}
};
// Passing an lvalue by value makes a non-elidable copy.
constexpr int PassByValue(Counter c) { return c.copies; }
static_assert(PassByValue(Counter(0)) == 0, "expect no copies");
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -fcxx-exceptions %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -fcxx-exceptions %s -fexperimental-new-constant-interpreter

// An explicitly-defaulted function may be declared constexpr only if it would
// have been implicitly declared as constexpr.
Expand Down