diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 84f7e6287609c..9a64ff93b27e9 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -3235,7 +3235,8 @@ bool Compiler::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)) @@ -3246,6 +3247,19 @@ bool Compiler::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(SrcObj)) { + if (!this->emitCheckFunctionDecl(Ctor, E)) + return false; + return this->visitInitializer(ME->getSubExpr()); + } + } + const Function *Func = getFunction(Ctor); if (!Func) diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index a2fb0fb331f8a..1f2ae92f6068b 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -919,33 +919,8 @@ 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. @@ -953,11 +928,10 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { 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) { @@ -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; @@ -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), @@ -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())) + return true; + + return diagnoseCallableDecl(S, OpPC, FD); +} + static void compileFunction(InterpState &S, const Function *Func) { const FunctionDecl *Definition = Func->getDecl()->getDefinition(); if (!Definition) diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 6877b03f5916b..c16408cef1fde 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -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); diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 1c17ad9e95d05..a2eaa61ea4306 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -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 *"; } @@ -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; diff --git a/clang/test/AST/ByteCode/cxx11.cpp b/clang/test/AST/ByteCode/cxx11.cpp index e283a7b42e554..753e51dfbfc1c 100644 --- a/clang/test/AST/ByteCode/cxx11.cpp +++ b/clang/test/AST/ByteCode/cxx11.cpp @@ -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"); diff --git a/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp b/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp index 849594307390f..4055a6a44699c 100644 --- a/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp +++ b/clang/test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp @@ -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.