[libcxx-commits] [libcxx] 72315d0 - Treat `std::move`, `forward`, etc. as builtins.
Richard Smith via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Apr 20 17:59:11 PDT 2022
Author: Richard Smith
Date: 2022-04-20T17:58:31-07:00
New Revision: 72315d02c432a0fe0acae9c96c69eac8d8e1a9f6
URL: https://github.com/llvm/llvm-project/commit/72315d02c432a0fe0acae9c96c69eac8d8e1a9f6
DIFF: https://github.com/llvm/llvm-project/commit/72315d02c432a0fe0acae9c96c69eac8d8e1a9f6.diff
LOG: Treat `std::move`, `forward`, etc. as builtins.
This is extended to all `std::` functions that take a reference to a
value and return a reference (or pointer) to that same value: `move`,
`forward`, `move_if_noexcept`, `as_const`, `addressof`, and the
libstdc++-specific function `__addressof`.
We still require these functions to be declared before they can be used,
but don't instantiate their definitions unless their addresses are
taken. Instead, code generation, constant evaluation, and static
analysis are given direct knowledge of their effect.
This change aims to reduce various costs associated with these functions
-- per-instantiation memory costs, compile time and memory costs due to
creating out-of-line copies and inlining them, code size at -O0, and so
on -- so that they are not substantially more expensive than a cast.
Most of these improvements are very small, but I measured a 3% decrease
in -O0 object file size for a simple C++ source file using the standard
library after this change.
We now automatically infer the `const` and `nothrow` attributes on these
now-builtin functions, in particular meaning that we get a warning for
an unused call to one of these functions.
In C++20 onwards, we disallow taking the addresses of these functions,
per the C++20 "addressable function" rule. In earlier language modes, a
compatibility warning is produced but the address can still be taken.
The same infrastructure is extended to the existing MSVC builtin
`__GetExceptionInfo`, which is now only recognized in namespace `std`
like it always should have been.
This is a re-commit of
fc3090109643af8d2da9822d0f99c84742b9c877,
a571f82a50416b767fd3cce0fb5027bb5dfec58c,
64c045e25b8471bbb572bd29159c294a82a86a2, and
de6ddaeef3aaa8a9ae3663c12cdb57d9afc0f906,
and reverts aa643f455a5362de7189eac630050d2c8aefe8f2.
This change also includes a workaround for users using libc++ 3.1 and
earlier (!!), as apparently happens on AIX, where std::move sometimes
returns by value.
Reviewed By: aaron.ballman
Differential Revision: https://reviews.llvm.org/D123345
Revert "Fixup D123950 to address revert of D123345"
This reverts commit aa643f455a5362de7189eac630050d2c8aefe8f2.
Added:
clang/test/CodeGenCXX/builtin-std-move.cpp
clang/test/SemaCXX/builtin-std-move-nobuiltin.cpp
clang/test/SemaCXX/builtin-std-move.cpp
clang/test/SemaCXX/libcxx_move_hack.cpp
Modified:
clang/docs/CommandGuide/clang.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/Builtins.def
clang/include/clang/Basic/Builtins.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/AST/ExprConstant.cpp
clang/lib/Analysis/BodyFarm.cpp
clang/lib/Basic/Builtins.cpp
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/CodeGen/CGCall.cpp
clang/lib/CodeGen/CGExpr.cpp
clang/lib/Sema/SemaChecking.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/test/Analysis/inner-pointer.cpp
clang/test/Analysis/use-after-move.cpp
clang/test/CodeGenCXX/builtins.cpp
clang/test/CodeGenCXX/microsoft-abi-throw.cpp
clang/test/SemaCXX/unqualified-std-call-fixits.cpp
clang/test/SemaCXX/unqualified-std-call.cpp
clang/test/SemaCXX/warn-consumed-analysis.cpp
clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
libcxx/test/std/utilities/utility/forward/forward.fail.cpp
Removed:
################################################################################
diff --git a/clang/docs/CommandGuide/clang.rst b/clang/docs/CommandGuide/clang.rst
index aec62789a43e6..658a30458043e 100644
--- a/clang/docs/CommandGuide/clang.rst
+++ b/clang/docs/CommandGuide/clang.rst
@@ -252,8 +252,24 @@ Language Selection and Mode Options
.. option:: -fno-builtin
- Disable special handling and optimizations of builtin functions like
- :c:func:`strlen` and :c:func:`malloc`.
+ Disable special handling and optimizations of well-known library functions,
+ like :c:func:`strlen` and :c:func:`malloc`.
+
+.. option:: -fno-builtin-<function>
+
+ Disable special handling and optimizations for the specific library function.
+ For example, ``-fno-builtin-strlen`` removes any special handling for the
+ :c:func:`strlen` library function.
+
+.. option:: -fno-builtin-std-<function>
+
+ Disable special handling and optimizations for the specific C++ standard
+ library function in namespace ``std``. For example,
+ ``-fno-builtin-std-move_if_noexcept`` removes any special handling for the
+ :cpp:func:`std::move_if_noexcept` library function.
+
+ For C standard library functions that the C++ standard library also provides
+ in namespace ``std``, use :option:`-fno-builtin-\<function\>` instead.
.. option:: -fmath-errno
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4e63effe5769f..cb5cad717e347 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -250,7 +250,10 @@ C2x Feature Support
C++ Language Changes in Clang
-----------------------------
-- ...
+- Improved ``-O0`` code generation for calls to ``std::move``, ``std::forward``,
+ ``std::move_if_noexcept``, ``std::addressof``, and ``std::as_const``. These
+ are now treated as compiler builtins and implemented directly, rather than
+ instantiating the definition from the standard library.
C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def
index 62e82cd36321d..c22957e14de7d 100644
--- a/clang/include/clang/Basic/Builtins.def
+++ b/clang/include/clang/Basic/Builtins.def
@@ -81,7 +81,9 @@
// builtin even if type doesn't match signature, and don't warn if we
// can't be sure the type is right
// F -> this is a libc/libm function with a '__builtin_' prefix added.
-// f -> this is a libc/libm function without the '__builtin_' prefix.
+// f -> this is a libc/libm function without a '__builtin_' prefix, or with
+// 'z', a C++ standard library function in namespace std::. This builtin
+// is disableable by '-fno-builtin-foo' / '-fno-builtin-std-foo'.
// h -> this function requires a specific header or an explicit declaration.
// i -> this is a runtime library implemented function without the
// '__builtin_' prefix. It will be implemented in compiler-rt or libgcc.
@@ -101,6 +103,7 @@
// V:N: -> requires vectors of at least N bits to be legal
// C<N,M_0,...,M_k> -> callback behavior: argument N is called with argument
// M_0, ..., M_k as payload
+// z -> this is a function in (possibly-versioned) namespace std
// FIXME: gcc has nonnull
#if defined(BUILTIN) && !defined(LIBBUILTIN)
@@ -919,7 +922,7 @@ LANGBUILTIN(__exception_info, "v*", "n", ALL_MS_LANGUAGES)
LANGBUILTIN(_exception_info, "v*", "n", ALL_MS_LANGUAGES)
LANGBUILTIN(__abnormal_termination, "i", "n", ALL_MS_LANGUAGES)
LANGBUILTIN(_abnormal_termination, "i", "n", ALL_MS_LANGUAGES)
-LANGBUILTIN(__GetExceptionInfo, "v*.", "ntu", ALL_MS_LANGUAGES)
+LANGBUILTIN(__GetExceptionInfo, "v*.", "zntu", ALL_MS_LANGUAGES)
LANGBUILTIN(_InterlockedAnd8, "ccD*c", "n", ALL_MS_LANGUAGES)
LANGBUILTIN(_InterlockedAnd16, "ssD*s", "n", ALL_MS_LANGUAGES)
LANGBUILTIN(_InterlockedAnd, "NiNiD*Ni", "n", ALL_MS_LANGUAGES)
@@ -1543,6 +1546,15 @@ LIBBUILTIN(_Block_object_assign, "vv*vC*iC", "f", "Blocks.h", ALL_LANGUAGES)
LIBBUILTIN(_Block_object_dispose, "vvC*iC", "f", "Blocks.h", ALL_LANGUAGES)
// FIXME: Also declare NSConcreteGlobalBlock and NSConcreteStackBlock.
+// C++ standard library builtins in namespace 'std'.
+LIBBUILTIN(addressof, "v*v&", "zfncTh", "memory", CXX_LANG)
+// Synonym for addressof used internally by libstdc++.
+LANGBUILTIN(__addressof, "v*v&", "zfncT", CXX_LANG)
+LIBBUILTIN(as_const, "v&v&", "zfncTh", "utility", CXX_LANG)
+LIBBUILTIN(forward, "v&v&", "zfncTh", "utility", CXX_LANG)
+LIBBUILTIN(move, "v&v&", "zfncTh", "utility", CXX_LANG)
+LIBBUILTIN(move_if_noexcept, "v&v&", "zfncTh", "utility", CXX_LANG)
+
// Annotation function
BUILTIN(__builtin_annotation, "v.", "tn")
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index 2926e0fa2c8d6..a82e15730f992 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -138,6 +138,10 @@ class Context {
/// Determines whether this builtin is a predefined libc/libm
/// function, such as "malloc", where we know the signature a
/// priori.
+ /// In C, such functions behave as if they are predeclared,
+ /// possibly with a warning on first use. In Objective-C and C++,
+ /// they do not, but they are recognized as builtins once we see
+ /// a declaration.
bool isPredefinedLibFunction(unsigned ID) const {
return strchr(getRecord(ID).Attributes, 'f') != nullptr;
}
@@ -156,6 +160,23 @@ class Context {
return strchr(getRecord(ID).Attributes, 'i') != nullptr;
}
+ /// Determines whether this builtin is a C++ standard library function
+ /// that lives in (possibly-versioned) namespace std, possibly a template
+ /// specialization, where the signature is determined by the standard library
+ /// declaration.
+ bool isInStdNamespace(unsigned ID) const {
+ return strchr(getRecord(ID).Attributes, 'z') != nullptr;
+ }
+
+ /// Determines whether this builtin can have its address taken with no
+ /// special action required.
+ bool isDirectlyAddressable(unsigned ID) const {
+ // Most standard library functions can have their addresses taken. C++
+ // standard library functions formally cannot in C++20 onwards, and when
+ // we allow it, we need to ensure we instantiate a definition.
+ return isPredefinedLibFunction(ID) && !isInStdNamespace(ID);
+ }
+
/// Determines whether this builtin has custom typechecking.
bool hasCustomTypechecking(unsigned ID) const {
return strchr(getRecord(ID).Attributes, 't') != nullptr;
@@ -237,10 +258,6 @@ class Context {
private:
const Info &getRecord(unsigned ID) const;
- /// Is this builtin supported according to the given language options?
- bool builtinIsSupported(const Builtin::Info &BuiltinInfo,
- const LangOptions &LangOpts);
-
/// Helper function for isPrintfLike and isScanfLike.
bool isLike(unsigned ID, unsigned &FormatIdx, bool &HasVAListArg,
const char *Fmt) const;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 2008f02bf9909..26e6db82a47f4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6590,6 +6590,15 @@ def warn_self_move : Warning<
"explicitly moving variable of type %0 to itself">,
InGroup<SelfMove>, DefaultIgnore;
+def err_builtin_move_forward_unsupported : Error<
+ "unsupported signature for %q0">;
+def err_use_of_unaddressable_function : Error<
+ "taking address of non-addressable standard library function">;
+// FIXME: This should also be in -Wc++23-compat once we have it.
+def warn_cxx20_compat_use_of_unaddressable_function : Warning<
+ "taking address of non-addressable standard library function "
+ "is incompatible with C++20">, InGroup<CXX20Compat>;
+
def warn_redundant_move_on_return : Warning<
"redundant move in return statement">,
InGroup<RedundantMove>, DefaultIgnore;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 6fe5d77a33fe5..3ad44d5e2da80 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8127,6 +8127,7 @@ class LValueExprEvaluator
bool VisitVarDecl(const Expr *E, const VarDecl *VD);
bool VisitUnaryPreIncDec(const UnaryOperator *UO);
+ bool VisitCallExpr(const CallExpr *E);
bool VisitDeclRefExpr(const DeclRefExpr *E);
bool VisitPredefinedExpr(const PredefinedExpr *E) { return Success(E); }
bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
@@ -8292,6 +8293,20 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
return Success(*V, E);
}
+bool LValueExprEvaluator::VisitCallExpr(const CallExpr *E) {
+ switch (E->getBuiltinCallee()) {
+ case Builtin::BIas_const:
+ case Builtin::BIforward:
+ case Builtin::BImove:
+ case Builtin::BImove_if_noexcept:
+ if (cast<FunctionDecl>(E->getCalleeDecl())->isConstexpr())
+ return Visit(E->getArg(0));
+ break;
+ }
+
+ return ExprEvaluatorBaseTy::VisitCallExpr(E);
+}
+
bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *E) {
// Walk through the expression to find the materialized temporary itself.
@@ -9070,6 +9085,8 @@ static bool isOneByteCharacterType(QualType T) {
bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
unsigned BuiltinOp) {
switch (BuiltinOp) {
+ case Builtin::BIaddressof:
+ case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
return evaluateLValue(E->getArg(0), Result);
case Builtin::BI__builtin_assume_aligned: {
diff --git a/clang/lib/Analysis/BodyFarm.cpp b/clang/lib/Analysis/BodyFarm.cpp
index 92c236ed9080c..95bed548c567a 100644
--- a/clang/lib/Analysis/BodyFarm.cpp
+++ b/clang/lib/Analysis/BodyFarm.cpp
@@ -20,6 +20,7 @@
#include "clang/AST/ExprObjC.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/Analysis/CodeInjector.h"
+#include "clang/Basic/Builtins.h"
#include "clang/Basic/OperatorKinds.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Debug.h"
@@ -86,6 +87,9 @@ class ASTMaker {
ImplicitCastExpr *makeImplicitCast(const Expr *Arg, QualType Ty,
CastKind CK = CK_LValueToRValue);
+ /// Create a cast to reference type.
+ CastExpr *makeReferenceCast(const Expr *Arg, QualType Ty);
+
/// Create an Objective-C bool literal.
ObjCBoolLiteralExpr *makeObjCBool(bool Val);
@@ -173,6 +177,16 @@ ImplicitCastExpr *ASTMaker::makeImplicitCast(const Expr *Arg, QualType Ty,
/* FPFeatures */ FPOptionsOverride());
}
+CastExpr *ASTMaker::makeReferenceCast(const Expr *Arg, QualType Ty) {
+ assert(Ty->isReferenceType());
+ return CXXStaticCastExpr::Create(
+ C, Ty.getNonReferenceType(),
+ Ty->isLValueReferenceType() ? VK_LValue : VK_XValue, CK_NoOp,
+ const_cast<Expr *>(Arg), /*CXXCastPath=*/nullptr,
+ /*Written=*/C.getTrivialTypeSourceInfo(Ty), FPOptionsOverride(),
+ SourceLocation(), SourceLocation(), SourceRange());
+}
+
Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) {
if (Arg->getType() == Ty)
return const_cast<Expr*>(Arg);
@@ -296,6 +310,22 @@ static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M,
/*FPFeatures=*/FPOptionsOverride());
}
+/// Create a fake body for 'std::move' or 'std::forward'. This is just:
+///
+/// \code
+/// return static_cast<return_type>(param);
+/// \endcode
+static Stmt *create_std_move_forward(ASTContext &C, const FunctionDecl *D) {
+ LLVM_DEBUG(llvm::dbgs() << "Generating body for std::move / std::forward\n");
+
+ ASTMaker M(C);
+
+ QualType ReturnType = D->getType()->castAs<FunctionType>()->getReturnType();
+ Expr *Param = M.makeDeclRefExpr(D->getParamDecl(0));
+ Expr *Cast = M.makeReferenceCast(Param, ReturnType);
+ return M.makeReturn(Cast);
+}
+
/// Create a fake body for std::call_once.
/// Emulates the following function body:
///
@@ -681,8 +711,20 @@ Stmt *BodyFarm::getBody(const FunctionDecl *D) {
FunctionFarmer FF;
- if (Name.startswith("OSAtomicCompareAndSwap") ||
- Name.startswith("objc_atomicCompareAndSwap")) {
+ if (unsigned BuiltinID = D->getBuiltinID()) {
+ switch (BuiltinID) {
+ case Builtin::BIas_const:
+ case Builtin::BIforward:
+ case Builtin::BImove:
+ case Builtin::BImove_if_noexcept:
+ FF = create_std_move_forward;
+ break;
+ default:
+ FF = nullptr;
+ break;
+ }
+ } else if (Name.startswith("OSAtomicCompareAndSwap") ||
+ Name.startswith("objc_atomicCompareAndSwap")) {
FF = create_OSAtomicCompareAndSwap;
} else if (Name == "call_once" && D->getDeclContext()->isStdNamespace()) {
FF = create_call_once;
diff --git a/clang/lib/Basic/Builtins.cpp b/clang/lib/Basic/Builtins.cpp
index 6d278e9c4a229..ef8bb562ac17e 100644
--- a/clang/lib/Basic/Builtins.cpp
+++ b/clang/lib/Basic/Builtins.cpp
@@ -48,18 +48,22 @@ void Builtin::Context::InitializeTarget(const TargetInfo &Target,
}
bool Builtin::Context::isBuiltinFunc(llvm::StringRef FuncName) {
- for (unsigned i = Builtin::NotBuiltin + 1; i != Builtin::FirstTSBuiltin; ++i)
- if (FuncName.equals(BuiltinInfo[i].Name))
+ bool InStdNamespace = FuncName.consume_front("std-");
+ for (unsigned i = Builtin::NotBuiltin + 1; i != Builtin::FirstTSBuiltin;
+ ++i) {
+ if (FuncName.equals(BuiltinInfo[i].Name) &&
+ (bool)strchr(BuiltinInfo[i].Attributes, 'z') == InStdNamespace)
return strchr(BuiltinInfo[i].Attributes, 'f') != nullptr;
+ }
return false;
}
-bool Builtin::Context::builtinIsSupported(const Builtin::Info &BuiltinInfo,
- const LangOptions &LangOpts) {
+/// Is this builtin supported according to the given language options?
+static bool builtinIsSupported(const Builtin::Info &BuiltinInfo,
+ const LangOptions &LangOpts) {
bool BuiltinsUnsupported =
- (LangOpts.NoBuiltin || LangOpts.isNoBuiltinFunc(BuiltinInfo.Name)) &&
- strchr(BuiltinInfo.Attributes, 'f');
+ LangOpts.NoBuiltin && strchr(BuiltinInfo.Attributes, 'f') != nullptr;
bool CorBuiltinsUnsupported =
!LangOpts.Coroutines && (BuiltinInfo.Langs & COR_LANG);
bool MathBuiltinsUnsupported =
@@ -111,6 +115,19 @@ void Builtin::Context::initializeBuiltins(IdentifierTable &Table,
for (unsigned i = 0, e = AuxTSRecords.size(); i != e; ++i)
Table.get(AuxTSRecords[i].Name)
.setBuiltinID(i + Builtin::FirstTSBuiltin + TSRecords.size());
+
+ // Step #4: Unregister any builtins specified by -fno-builtin-foo.
+ for (llvm::StringRef Name : LangOpts.NoBuiltinFuncs) {
+ bool InStdNamespace = Name.consume_front("std-");
+ auto NameIt = Table.find(Name);
+ if (NameIt != Table.end()) {
+ unsigned ID = NameIt->second->getBuiltinID();
+ if (ID != Builtin::NotBuiltin && isPredefinedLibFunction(ID) &&
+ isInStdNamespace(ID) == InStdNamespace) {
+ Table.get(Name).setBuiltinID(Builtin::NotBuiltin);
+ }
+ }
+ }
}
unsigned Builtin::Context::getRequiredVectorWidth(unsigned ID) const {
@@ -190,8 +207,7 @@ bool Builtin::Context::performsCallback(unsigned ID,
}
bool Builtin::Context::canBeRedeclared(unsigned ID) const {
- return ID == Builtin::NotBuiltin ||
- ID == Builtin::BI__va_start ||
- (!hasReferenceArgsOrResult(ID) &&
- !hasCustomTypechecking(ID));
+ return ID == Builtin::NotBuiltin || ID == Builtin::BI__va_start ||
+ (!hasReferenceArgsOrResult(ID) && !hasCustomTypechecking(ID)) ||
+ isInStdNamespace(ID);
}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 17adc9ba75079..fc2d32f3e26fe 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -2271,8 +2271,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
ReturnValueSlot ReturnValue) {
const FunctionDecl *FD = GD.getDecl()->getAsFunction();
// See if we can constant fold this builtin. If so, don't emit it at all.
+ // TODO: Extend this handling to all builtin calls that we can constant-fold.
Expr::EvalResult Result;
- if (E->EvaluateAsRValue(Result, CGM.getContext()) &&
+ if (E->isPRValue() && E->EvaluateAsRValue(Result, CGM.getContext()) &&
!Result.hasSideEffects()) {
if (Result.Val.isInt())
return RValue::get(llvm::ConstantInt::get(getLLVMContext(),
@@ -4566,6 +4567,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Carry);
}
+ case Builtin::BIaddressof:
+ case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
return RValue::get(EmitLValue(E->getArg(0)).getPointer(*this));
case Builtin::BI__builtin_function_start:
@@ -4725,6 +4728,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
}
break;
+ // C++ std:: builtins.
+ case Builtin::BImove:
+ case Builtin::BImove_if_noexcept:
+ case Builtin::BIforward:
+ case Builtin::BIas_const:
+ return RValue::get(EmitLValue(E->getArg(0)).getPointer(*this));
case Builtin::BI__GetExceptionInfo: {
if (llvm::GlobalVariable *GV =
CGM.getCXXABI().getThrowInfo(FD->getParamDecl(0)->getType()))
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index e6a5fd1f4f985..d69455d650f43 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -1805,6 +1805,8 @@ void CodeGenModule::getDefaultFunctionAttributes(StringRef Name,
if (AttrOnCallSite) {
// Attributes that should go on the call site only.
+ // FIXME: Look for 'BuiltinAttr' on the function rather than re-checking
+ // the -fno-builtin-foo list.
if (!CodeGenOpts.SimplifyLibCalls || LangOpts.isNoBuiltinFunc(Name))
FuncAttrs.addAttribute(llvm::Attribute::NoBuiltin);
if (!CodeGenOpts.TrapFuncName.empty())
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index b2947e3927165..fbe1586001b19 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1175,6 +1175,8 @@ Address CodeGenFunction::EmitPointerWithAlignment(const Expr *E,
switch (Call->getBuiltinCallee()) {
default:
break;
+ case Builtin::BIaddressof:
+ case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof: {
LValue LV = EmitLValue(Call->getArg(0));
if (BaseInfo) *BaseInfo = LV.getBaseInfo();
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 34de3df7a80c3..467edd3321b9d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2130,6 +2130,32 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
TheCall->setType(Context.VoidPtrTy);
break;
+ case Builtin::BIaddressof:
+ case Builtin::BI__addressof:
+ case Builtin::BIforward:
+ case Builtin::BImove:
+ case Builtin::BImove_if_noexcept:
+ case Builtin::BIas_const: {
+ // These are all expected to be of the form
+ // T &/&&/* f(U &/&&)
+ // where T and U only
diff er in qualification.
+ if (checkArgCount(*this, TheCall, 1))
+ return ExprError();
+ QualType Param = FDecl->getParamDecl(0)->getType();
+ QualType Result = FDecl->getReturnType();
+ bool ReturnsPointer = BuiltinID == Builtin::BIaddressof ||
+ BuiltinID == Builtin::BI__addressof;
+ if (!(Param->isReferenceType() &&
+ (ReturnsPointer ? Result->isPointerType()
+ : Result->isReferenceType()) &&
+ Context.hasSameUnqualifiedType(Param->getPointeeType(),
+ Result->getPointeeType()))) {
+ Diag(TheCall->getBeginLoc(), diag::err_builtin_move_forward_unsupported)
+ << FDecl;
+ return ExprError();
+ }
+ break;
+ }
// OpenCL v2.0, s6.13.16 - Pipe functions
case Builtin::BIread_pipe:
case Builtin::BIwrite_pipe:
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 30164e56120ce..0c8b7fcd306b6 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -9276,6 +9276,32 @@ static Scope *getTagInjectionScope(Scope *S, const LangOptions &LangOpts) {
return S;
}
+/// Determine whether a declaration matches a known function in namespace std.
+static bool isStdBuiltin(ASTContext &Ctx, FunctionDecl *FD,
+ unsigned BuiltinID) {
+ switch (BuiltinID) {
+ case Builtin::BI__GetExceptionInfo:
+ // No type checking whatsoever.
+ return Ctx.getTargetInfo().getCXXABI().isMicrosoft();
+
+ case Builtin::BIaddressof:
+ case Builtin::BI__addressof:
+ case Builtin::BIforward:
+ case Builtin::BImove:
+ case Builtin::BImove_if_noexcept:
+ case Builtin::BIas_const: {
+ // Ensure that we don't treat the algorithm
+ // OutputIt std::move(InputIt, InputIt, OutputIt)
+ // as the builtin std::move.
+ const auto *FPT = FD->getType()->castAs<FunctionProtoType>();
+ return FPT->getNumParams() == 1 && !FPT->isVariadic();
+ }
+
+ default:
+ return false;
+ }
+}
+
NamedDecl*
Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
TypeSourceInfo *TInfo, LookupResult &Previous,
@@ -10128,28 +10154,30 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
// If this is the first declaration of a library builtin function, add
// attributes as appropriate.
- if (!D.isRedeclaration() &&
- NewFD->getDeclContext()->getRedeclContext()->isFileContext()) {
+ if (!D.isRedeclaration()) {
if (IdentifierInfo *II = Previous.getLookupName().getAsIdentifierInfo()) {
if (unsigned BuiltinID = II->getBuiltinID()) {
- if (NewFD->getLanguageLinkage() == CLanguageLinkage) {
- // Validate the type matches unless this builtin is specified as
- // matching regardless of its declared type.
- if (Context.BuiltinInfo.allowTypeMismatch(BuiltinID)) {
- NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
- } else {
- ASTContext::GetBuiltinTypeError Error;
- LookupNecessaryTypesForBuiltin(S, BuiltinID);
- QualType BuiltinType = Context.GetBuiltinType(BuiltinID, Error);
-
- if (!Error && !BuiltinType.isNull() &&
- Context.hasSameFunctionTypeIgnoringExceptionSpec(
- NewFD->getType(), BuiltinType))
+ bool InStdNamespace = Context.BuiltinInfo.isInStdNamespace(BuiltinID);
+ if (!InStdNamespace &&
+ NewFD->getDeclContext()->getRedeclContext()->isFileContext()) {
+ if (NewFD->getLanguageLinkage() == CLanguageLinkage) {
+ // Validate the type matches unless this builtin is specified as
+ // matching regardless of its declared type.
+ if (Context.BuiltinInfo.allowTypeMismatch(BuiltinID)) {
NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
+ } else {
+ ASTContext::GetBuiltinTypeError Error;
+ LookupNecessaryTypesForBuiltin(S, BuiltinID);
+ QualType BuiltinType = Context.GetBuiltinType(BuiltinID, Error);
+
+ if (!Error && !BuiltinType.isNull() &&
+ Context.hasSameFunctionTypeIgnoringExceptionSpec(
+ NewFD->getType(), BuiltinType))
+ NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
+ }
}
- } else if (BuiltinID == Builtin::BI__GetExceptionInfo &&
- Context.getTargetInfo().getCXXABI().isMicrosoft()) {
- // FIXME: We should consider this a builtin only in the std namespace.
+ } else if (InStdNamespace && NewFD->isInStdNamespace() &&
+ isStdBuiltin(Context, NewFD, BuiltinID)) {
NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
}
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 273de0867e3ed..3bd83b8136869 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -3396,7 +3396,7 @@ ExprResult Sema::BuildDeclarationNameExpr(
case Decl::Function: {
if (unsigned BID = cast<FunctionDecl>(VD)->getBuiltinID()) {
- if (!Context.BuiltinInfo.isPredefinedLibFunction(BID)) {
+ if (!Context.BuiltinInfo.isDirectlyAddressable(BID)) {
type = Context.BuiltinFnTy;
valueKind = VK_PRValue;
break;
@@ -20480,7 +20480,8 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) {
auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
if (DRE) {
auto *FD = cast<FunctionDecl>(DRE->getDecl());
- if (FD->getBuiltinID() == Builtin::BI__noop) {
+ unsigned BuiltinID = FD->getBuiltinID();
+ if (BuiltinID == Builtin::BI__noop) {
E = ImpCastExprToType(E, Context.getPointerType(FD->getType()),
CK_BuiltinFnToFnPtr)
.get();
@@ -20488,6 +20489,36 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) {
VK_PRValue, SourceLocation(),
FPOptionsOverride());
}
+
+ if (Context.BuiltinInfo.isInStdNamespace(BuiltinID)) {
+ // Any use of these other than a direct call is ill-formed as of C++20,
+ // because they are not addressable functions. In earlier language
+ // modes, warn and force an instantiation of the real body.
+ Diag(E->getBeginLoc(),
+ getLangOpts().CPlusPlus20
+ ? diag::err_use_of_unaddressable_function
+ : diag::warn_cxx20_compat_use_of_unaddressable_function);
+ if (FD->isImplicitlyInstantiable()) {
+ // Require a definition here because a normal attempt at
+ // instantiation for a builtin will be ignored, and we won't try
+ // again later. We assume that the definition of the template
+ // precedes this use.
+ InstantiateFunctionDefinition(E->getBeginLoc(), FD,
+ /*Recursive=*/false,
+ /*DefinitionRequired=*/true,
+ /*AtEndOfTU=*/false);
+ }
+ // Produce a properly-typed reference to the function.
+ CXXScopeSpec SS;
+ SS.Adopt(DRE->getQualifierLoc());
+ TemplateArgumentListInfo TemplateArgs;
+ DRE->copyTemplateArgumentsInto(TemplateArgs);
+ return BuildDeclRefExpr(
+ FD, FD->getType(), VK_LValue, DRE->getNameInfo(),
+ DRE->hasQualifier() ? &SS : nullptr, DRE->getFoundDecl(),
+ DRE->getTemplateKeywordLoc(),
+ DRE->hasExplicitTemplateArgs() ? &TemplateArgs : nullptr);
+ }
}
Diag(E->getBeginLoc(), diag::err_builtin_fn_use);
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 79b670a1ec735..7b3a18a503816 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4237,6 +4237,14 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
return ExprError();
From = FixOverloadedFunctionReference(From, Found, Fn);
+
+ // We might get back another placeholder expression if we resolved to a
+ // builtin.
+ ExprResult Checked = CheckPlaceholderExpr(From);
+ if (Checked.isInvalid())
+ return ExprError();
+
+ From = Checked.get();
FromType = From->getType();
}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index c7048225c9d43..2a49627ecf106 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8215,6 +8215,10 @@ ExprResult InitializationSequence::Perform(Sema &S,
CurInit = S.FixOverloadedFunctionReference(CurInit,
Step->Function.FoundDecl,
Step->Function.Function);
+ // We might get back another placeholder expression if we resolved to a
+ // builtin.
+ if (!CurInit.isInvalid())
+ CurInit = S.CheckPlaceholderExpr(CurInit.get());
break;
case SK_CastDerivedToBasePRValue:
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 33271609a0081..1add971a81a02 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1747,13 +1747,6 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
"Non-address-of operator for overloaded function expression");
FromType = S.Context.getPointerType(FromType);
}
-
- // Check that we've computed the proper type after overload resolution.
- // FIXME: FixOverloadedFunctionReference has side-effects; we shouldn't
- // be calling it from within an NDEBUG block.
- assert(S.Context.hasSameType(
- FromType,
- S.FixOverloadedFunctionReference(From, AccessPair, Fn)->getType()));
} else {
return false;
}
@@ -15188,10 +15181,9 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
if (SubExpr == UnOp->getSubExpr())
return UnOp;
- return UnaryOperator::Create(
- Context, SubExpr, UO_AddrOf, Context.getPointerType(SubExpr->getType()),
- VK_PRValue, OK_Ordinary, UnOp->getOperatorLoc(), false,
- CurFPFeatureOverrides());
+ // FIXME: This can't currently fail, but in principle it could.
+ return CreateBuiltinUnaryOp(UnOp->getOperatorLoc(), UO_AddrOf, SubExpr)
+ .get();
}
if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(E)) {
@@ -15202,10 +15194,20 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
TemplateArgs = &TemplateArgsBuffer;
}
- DeclRefExpr *DRE =
- BuildDeclRefExpr(Fn, Fn->getType(), VK_LValue, ULE->getNameInfo(),
- ULE->getQualifierLoc(), Found.getDecl(),
- ULE->getTemplateKeywordLoc(), TemplateArgs);
+ QualType Type = Fn->getType();
+ ExprValueKind ValueKind = getLangOpts().CPlusPlus ? VK_LValue : VK_PRValue;
+
+ // FIXME: Duplicated from BuildDeclarationNameExpr.
+ if (unsigned BID = Fn->getBuiltinID()) {
+ if (!Context.BuiltinInfo.isDirectlyAddressable(BID)) {
+ Type = Context.BuiltinFnTy;
+ ValueKind = VK_PRValue;
+ }
+ }
+
+ DeclRefExpr *DRE = BuildDeclRefExpr(
+ Fn, Type, ValueKind, ULE->getNameInfo(), ULE->getQualifierLoc(),
+ Found.getDecl(), ULE->getTemplateKeywordLoc(), TemplateArgs);
DRE->setHadMultipleCandidates(ULE->getNumDecls() > 1);
return DRE;
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 9d0dc8cad46b8..77c5677b65c87 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -621,6 +621,31 @@ static bool isRelevantAttr(Sema &S, const Decl *D, const Attr *A) {
return true;
}
+ if (const auto *BA = dyn_cast<BuiltinAttr>(A)) {
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
+ switch (BA->getID()) {
+ case Builtin::BIforward:
+ // Do not treat 'std::forward' as a builtin if it takes an rvalue reference
+ // type and returns an lvalue reference type. The library implementation
+ // will produce an error in this case; don't get in its way.
+ if (FD && FD->getNumParams() >= 1 &&
+ FD->getParamDecl(0)->getType()->isRValueReferenceType() &&
+ FD->getReturnType()->isLValueReferenceType()) {
+ return false;
+ }
+ LLVM_FALLTHROUGH;
+ case Builtin::BImove:
+ case Builtin::BImove_if_noexcept:
+ // HACK: Super-old versions of libc++ (3.1 and earlier) provide
+ // std::forward and std::move overloads that sometimes return by value
+ // instead of by reference when building in C++98 mode. Don't treat such
+ // cases as builtins.
+ if (FD && !FD->getReturnType()->isReferenceType())
+ return false;
+ break;
+ }
+ }
+
return true;
}
@@ -4771,6 +4796,12 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
if (TSK == TSK_ExplicitSpecialization)
return;
+ // Never implicitly instantiate a builtin; we don't actually need a function
+ // body.
+ if (Function->getBuiltinID() && TSK == TSK_ImplicitInstantiation &&
+ !DefinitionRequired)
+ return;
+
// Don't instantiate a definition if we already have one.
const FunctionDecl *ExistingDefn = nullptr;
if (Function->isDefined(ExistingDefn,
diff --git a/clang/test/Analysis/inner-pointer.cpp b/clang/test/Analysis/inner-pointer.cpp
index 920a1fe8dcc9d..5db17a952f90b 100644
--- a/clang/test/Analysis/inner-pointer.cpp
+++ b/clang/test/Analysis/inner-pointer.cpp
@@ -379,7 +379,7 @@ void func_addressof() {
const char *c;
std::string s;
c = s.c_str();
- addressof(s);
+ (void)addressof(s);
consume(c); // no-warning
}
diff --git a/clang/test/Analysis/use-after-move.cpp b/clang/test/Analysis/use-after-move.cpp
index d1278cad4c4f2..ce9b26f15360e 100644
--- a/clang/test/Analysis/use-after-move.cpp
+++ b/clang/test/Analysis/use-after-move.cpp
@@ -244,7 +244,7 @@ void reinitializationTest(int i) {
A a;
if (i == 1) { // peaceful-note 2 {{'i' is not equal to 1}}
// peaceful-note at -1 2 {{Taking false branch}}
- std::move(a);
+ (void)std::move(a);
}
if (i == 2) { // peaceful-note 2 {{'i' is not equal to 2}}
// peaceful-note at -1 2 {{Taking false branch}}
@@ -494,7 +494,7 @@ void templateArgIsNotUseTest() {
// Moves of global variables are not reported.
A global_a;
void globalVariablesTest() {
- std::move(global_a);
+ (void)std::move(global_a);
global_a.foo(); // no-warning
}
diff --git a/clang/test/CodeGenCXX/builtin-std-move.cpp b/clang/test/CodeGenCXX/builtin-std-move.cpp
new file mode 100644
index 0000000000000..55a96dac4c57c
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-std-move.cpp
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - -std=c++17 %s | FileCheck %s --implicit-check-not=@_ZSt4move
+
+namespace std {
+ template<typename T> constexpr T &&move(T &val) { return static_cast<T&&>(val); }
+ template<typename T> constexpr T &&move_if_noexcept(T &val);
+ template<typename T> constexpr T &&forward(T &val);
+ template<typename T> constexpr const T &as_const(T &val);
+
+ // Not the builtin.
+ template<typename T, typename U> T move(U source, U source_end, T dest);
+}
+
+class T {};
+extern "C" void take(T &&);
+extern "C" void take_lval(const T &);
+
+T a;
+
+// Check emission of a constant-evaluated call.
+// CHECK-DAG: @move_a = constant ptr @a
+T &&move_a = std::move(a);
+// CHECK-DAG: @move_if_noexcept_a = constant ptr @a
+T &&move_if_noexcept_a = std::move_if_noexcept(a);
+// CHECK-DAG: @forward_a = constant ptr @a
+T &forward_a = std::forward<T&>(a);
+
+// Check emission of a non-constant call.
+// CHECK-LABEL: define {{.*}} void @test
+extern "C" void test(T &t) {
+ // CHECK: store ptr %{{.*}}, ptr %[[T_REF:[^,]*]]
+ // CHECK: %0 = load ptr, ptr %[[T_REF]]
+ // CHECK: call void @take(ptr {{.*}} %0)
+ take(std::move(t));
+ // CHECK: %1 = load ptr, ptr %[[T_REF]]
+ // CHECK: call void @take(ptr {{.*}} %1)
+ take(std::move_if_noexcept(t));
+ // CHECK: %2 = load ptr, ptr %[[T_REF]]
+ // CHECK: call void @take(ptr {{.*}} %2)
+ take(std::forward<T&&>(t));
+ // CHECK: %3 = load ptr, ptr %[[T_REF]]
+ // CHECK: call void @take_lval(ptr {{.*}} %3)
+ take_lval(std::as_const<T&&>(t));
+
+ // CHECK: call {{.*}} @_ZSt4moveI1TS0_ET_T0_S2_S1_
+ std::move(t, t, t);
+}
+
+// CHECK: declare {{.*}} @_ZSt4moveI1TS0_ET_T0_S2_S1_
+
+// Check that we instantiate and emit if the address is taken.
+// CHECK-LABEL: define {{.*}} @use_address
+extern "C" void *use_address() {
+ // CHECK: ret {{.*}} @_ZSt4moveIiEOT_RS0_
+ return (void*)&std::move<int>;
+}
+
+// CHECK: define {{.*}} ptr @_ZSt4moveIiEOT_RS0_(ptr
+
+extern "C" void take_const_int_rref(const int &&);
+// CHECK-LABEL: define {{.*}} @move_const_int(
+extern "C" void move_const_int() {
+ // CHECK: store i32 5, ptr %[[N_ADDR:[^,]*]]
+ const int n = 5;
+ // CHECK: call {{.*}} @take_const_int_rref(ptr {{.*}} %[[N_ADDR]])
+ take_const_int_rref(std::move(n));
+}
diff --git a/clang/test/CodeGenCXX/builtins.cpp b/clang/test/CodeGenCXX/builtins.cpp
index c1c8b51cbae6d..56a29eac2490c 100644
--- a/clang/test/CodeGenCXX/builtins.cpp
+++ b/clang/test/CodeGenCXX/builtins.cpp
@@ -30,6 +30,24 @@ S *addressof(bool b, S &s, S &t) {
return __builtin_addressof(b ? s : t);
}
+namespace std { template<typename T> T *addressof(T &); }
+
+// CHECK: define {{.*}} @_Z13std_addressofbR1SS0_(
+S *std_addressof(bool b, S &s, S &t) {
+ // CHECK: %[[LVALUE:.*]] = phi
+ // CHECK: ret {{.*}}* %[[LVALUE]]
+ return std::addressof(b ? s : t);
+}
+
+namespace std { template<typename T> T *__addressof(T &); }
+
+// CHECK: define {{.*}} @_Z15std___addressofbR1SS0_(
+S *std___addressof(bool b, S &s, S &t) {
+ // CHECK: %[[LVALUE:.*]] = phi
+ // CHECK: ret {{.*}}* %[[LVALUE]]
+ return std::__addressof(b ? s : t);
+}
+
extern "C" int __builtin_abs(int); // #1
long __builtin_abs(long); // #2
extern "C" int __builtin_abs(int); // #3
diff --git a/clang/test/CodeGenCXX/microsoft-abi-throw.cpp b/clang/test/CodeGenCXX/microsoft-abi-throw.cpp
index 92849598cf063..aa79935e42ded 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-throw.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-throw.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -no-opaque-pointers -emit-llvm -o - -triple=i386-pc-win32 -std=c++11 %s -fcxx-exceptions -fms-extensions | FileCheck %s
-// RUN: %clang_cc1 -no-opaque-pointers -emit-llvm -o - -triple=i386-pc-win32 -std=c++11 %s -fcxx-exceptions -fms-extensions -DSTD | FileCheck %s
// CHECK-DAG: @"??_R0?AUY@@@8" = linkonce_odr global %rtti.TypeDescriptor7 { i8** @"??_7type_info@@6B@", i8* null, [8 x i8] c".?AUY@@\00" }, comdat
// CHECK-DAG: @"_CT??_R0?AUY@@@8??0Y@@QAE at ABU0@@Z8" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 4, i8* bitcast (%rtti.TypeDescriptor7* @"??_R0?AUY@@@8" to i8*), i32 0, i32 -1, i32 0, i32 8, i8* bitcast (%struct.Y* (%struct.Y*, %struct.Y*, i32)* @"??0Y@@QAE at ABU0@@Z" to i8*) }, section ".xdata", comdat
@@ -134,15 +133,10 @@ void h() {
throw nullptr;
}
-#ifdef STD
namespace std {
template <typename T>
void *__GetExceptionInfo(T);
}
-#else
-template <typename T>
-void *__GetExceptionInfo(T);
-#endif
using namespace std;
void *GetExceptionInfo_test0() {
diff --git a/clang/test/SemaCXX/builtin-std-move-nobuiltin.cpp b/clang/test/SemaCXX/builtin-std-move-nobuiltin.cpp
new file mode 100644
index 0000000000000..9185bd87bf8ab
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-std-move-nobuiltin.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s -DBUILTIN=builtin
+// RUN: %clang_cc1 -std=c++20 -verify %s -DBUILTIN=nobuiltin -fno-builtin
+// RUN: %clang_cc1 -std=c++20 -verify %s -DBUILTIN=nobuiltin -fno-builtin-std-move -fno-builtin-std-move_if_noexcept -fno-builtin-std-forward
+// RUN: %clang_cc1 -std=c++20 -verify %s -DBUILTIN=nobuiltin -ffreestanding
+// expected-no-diagnostics
+
+int nobuiltin;
+
+namespace std {
+ template<typename T> constexpr T &&move(T &x) { return (T&&)nobuiltin; }
+ template<typename T> constexpr T &&move_if_noexcept(T &x) { return (T&&)nobuiltin; }
+ template<typename T> constexpr T &&forward(T &x) { return (T&&)nobuiltin; }
+}
+
+template<typename T> constexpr T *addr(T &&r) { return &r; }
+
+int builtin;
+static_assert(addr(std::move(builtin)) == &BUILTIN);
+static_assert(addr(std::move_if_noexcept(builtin)) == &BUILTIN);
+static_assert(addr(std::forward(builtin)) == &BUILTIN);
diff --git a/clang/test/SemaCXX/builtin-std-move.cpp b/clang/test/SemaCXX/builtin-std-move.cpp
new file mode 100644
index 0000000000000..f220be972a350
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-std-move.cpp
@@ -0,0 +1,138 @@
+// RUN: %clang_cc1 -std=c++17 -verify %s
+// RUN: %clang_cc1 -std=c++17 -verify %s -DNO_CONSTEXPR
+// RUN: %clang_cc1 -std=c++20 -verify %s
+
+namespace std {
+#ifndef NO_CONSTEXPR
+#define CONSTEXPR constexpr
+#else
+#define CONSTEXPR
+#endif
+
+ template<typename T> CONSTEXPR T &&move(T &x) {
+ static_assert(T::moveable, "instantiated move"); // expected-error {{no member named 'moveable' in 'B'}}
+ // expected-error at -1 {{no member named 'moveable' in 'C'}}
+ return static_cast<T&&>(x);
+ }
+
+ // Unrelated move functions are not the builtin.
+ template<typename T> CONSTEXPR int move(T, T) { return 5; }
+
+ template<typename T, bool Rref> struct ref { using type = T&; };
+ template<typename T> struct ref<T, true> { using type = T&&; };
+
+ template<typename T> CONSTEXPR auto move_if_noexcept(T &x) -> typename ref<T, noexcept(T(static_cast<T&&>(x)))>::type {
+ static_assert(T::moveable, "instantiated move_if_noexcept"); // expected-error {{no member named 'moveable' in 'B'}}
+ return static_cast<typename ref<T, noexcept(T(static_cast<T&&>(x)))>::type>(x);
+ }
+
+ template<typename T> struct remove_reference { using type = T; };
+ template<typename T> struct remove_reference<T&> { using type = T; };
+ template<typename T> struct remove_reference<T&&> { using type = T; };
+
+ template<typename T> struct is_lvalue_reference { static constexpr bool value = false; };
+ template<typename T> struct is_lvalue_reference<T&> { static constexpr bool value = true; };
+
+ template<typename T> CONSTEXPR T &&forward(typename remove_reference<T>::type &x) {
+ static_assert(T::moveable, "instantiated forward"); // expected-error {{no member named 'moveable' in 'B'}}
+ // expected-error at -1 {{no member named 'moveable' in 'C'}}
+ return static_cast<T&&>(x);
+ }
+ template<typename T> CONSTEXPR T &&forward(typename remove_reference<T>::type &&x) {
+ static_assert(!is_lvalue_reference<T>::value, "should not forward rval as lval"); // expected-error {{static_assert failed}}
+ return static_cast<T&&>(x);
+ }
+
+ template<typename T> CONSTEXPR const T &as_const(T &x) {
+ static_assert(T::moveable, "instantiated as_const"); // expected-error {{no member named 'moveable' in 'B'}}
+ return x;
+ }
+
+ template<typename T> CONSTEXPR T *addressof(T &x) {
+ static_assert(T::moveable, "instantiated addressof"); // expected-error {{no member named 'moveable' in 'B'}}
+ return __builtin_addressof(x);
+ }
+
+ template<typename T> CONSTEXPR T *__addressof(T &x) {
+ static_assert(T::moveable, "instantiated __addressof"); // expected-error {{no member named 'moveable' in 'B'}}
+ return __builtin_addressof(x);
+ }
+}
+
+// Note: this doesn't have a 'moveable' member. Instantiation of the above
+// functions will fail if it's attempted.
+struct A {};
+constexpr bool f(A a) { // #f
+ A &&move = std::move(a); // #call
+ A &&move_if_noexcept = std::move_if_noexcept(a);
+ A &&forward1 = std::forward<A>(a);
+ A &forward2 = std::forward<A&>(a);
+ const A &as_const = std::as_const(a);
+ A *addressof = std::addressof(a);
+ A *addressof2 = std::__addressof(a);
+ return &move == &a && &move_if_noexcept == &a &&
+ &forward1 == &a && &forward2 == &a &&
+ &as_const == &a && addressof == &a &&
+ addressof2 == &a && std::move(a, a) == 5;
+}
+
+#ifndef NO_CONSTEXPR
+static_assert(f({}), "should be constexpr");
+#else
+// expected-error@#f {{never produces a constant expression}}
+// expected-note@#call {{}}
+#endif
+
+A &forward_rval_as_lval() {
+ std::forward<A&&>(A()); // expected-warning {{const attribute}}
+ return std::forward<A&>(A()); // expected-note {{instantiation of}}
+}
+
+struct B {};
+B &&(*pMove)(B&) = std::move; // #1 expected-note {{instantiation of}}
+B &&(*pMoveIfNoexcept)(B&) = &std::move_if_noexcept; // #2 expected-note {{instantiation of}}
+B &&(*pForward)(B&) = &std::forward<B>; // #3 expected-note {{instantiation of}}
+const B &(*pAsConst)(B&) = &std::as_const; // #4 expected-note {{instantiation of}}
+B *(*pAddressof)(B&) = &std::addressof; // #5 expected-note {{instantiation of}}
+B *(*pUnderUnderAddressof)(B&) = &std::__addressof; // #6 expected-note {{instantiation of}}
+int (*pUnrelatedMove)(B, B) = std::move;
+
+struct C {};
+C &&(&rMove)(C&) = std::move; // #7 expected-note {{instantiation of}}
+C &&(&rForward)(C&) = std::forward<C>; // #8 expected-note {{instantiation of}}
+int (&rUnrelatedMove)(B, B) = std::move;
+
+#if __cplusplus <= 201703L
+// expected-warning@#1 {{non-addressable}}
+// expected-warning@#2 {{non-addressable}}
+// expected-warning@#3 {{non-addressable}}
+// expected-warning@#4 {{non-addressable}}
+// expected-warning@#5 {{non-addressable}}
+// expected-warning@#6 {{non-addressable}}
+// expected-warning@#7 {{non-addressable}}
+// expected-warning@#8 {{non-addressable}}
+#else
+// expected-error@#1 {{non-addressable}}
+// expected-error@#2 {{non-addressable}}
+// expected-error@#3 {{non-addressable}}
+// expected-error@#4 {{non-addressable}}
+// expected-error@#5 {{non-addressable}}
+// expected-error@#6 {{non-addressable}}
+// expected-error@#7 {{non-addressable}}
+// expected-error@#8 {{non-addressable}}
+#endif
+
+void attribute_const() {
+ int n;
+ std::move(n); // expected-warning {{ignoring return value}}
+ std::move_if_noexcept(n); // expected-warning {{ignoring return value}}
+ std::forward<int>(n); // expected-warning {{ignoring return value}}
+ std::addressof(n); // expected-warning {{ignoring return value}}
+ std::__addressof(n); // expected-warning {{ignoring return value}}
+ std::as_const(n); // expected-warning {{ignoring return value}}
+}
+
+namespace std {
+ template<typename T> int &move(T);
+}
+int bad_signature = std::move(0); // expected-error {{unsupported signature for 'std::move<int>'}}
diff --git a/clang/test/SemaCXX/libcxx_move_hack.cpp b/clang/test/SemaCXX/libcxx_move_hack.cpp
new file mode 100644
index 0000000000000..5c48ed2db6427
--- /dev/null
+++ b/clang/test/SemaCXX/libcxx_move_hack.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++98 -verify
+// expected-no-diagnostics
+
+// This is a test for a hack in Clang that works around an issue with libc++
+// 3.1's std::move and std::forward implementation. When emulating these
+// functions in C++98 mode, libc++ 3.1 has a "fake rvalue reference" type, and
+// std::move will return by value when given an instance of that type.
+
+namespace std {
+ struct rv {};
+
+ template<bool B, typename T> struct enable_if;
+ template<typename T> struct enable_if<true, T> { typedef T type; };
+
+ template<typename T> typename enable_if<__is_convertible(T, rv), T>::type move(T &);
+ template<typename T> typename enable_if<!__is_convertible(T, rv), T&>::type move(T &);
+
+ template<typename U, typename T> typename enable_if<__is_convertible(T, rv), U>::type forward(T &);
+ template<typename U, typename T> typename enable_if<!__is_convertible(T, rv), U&>::type forward(T &);
+}
+
+struct A {};
+void f(A a, std::rv rv) {
+ a = std::move(a);
+ rv = std::move(rv);
+
+ a = std::forward<A>(a);
+ rv = std::forward<std::rv>(rv);
+
+ a = std::forward<A&>(a);
+ rv = std::forward<std::rv&>(rv);
+}
diff --git a/clang/test/SemaCXX/unqualified-std-call-fixits.cpp b/clang/test/SemaCXX/unqualified-std-call-fixits.cpp
index 0b2d70c0360a3..d6f8e5e2b95ed 100644
--- a/clang/test/SemaCXX/unqualified-std-call-fixits.cpp
+++ b/clang/test/SemaCXX/unqualified-std-call-fixits.cpp
@@ -6,9 +6,9 @@
namespace std {
-void move(auto &&a) {}
+int &&move(auto &&a) { return a; }
-void forward(auto &a) {}
+int &&forward(auto &a) { return a; }
} // namespace std
@@ -16,8 +16,8 @@ using namespace std;
void f() {
int i = 0;
- move(i); // expected-warning {{unqualified call to std::move}}
- // CHECK: {{^}} std::
- forward(i); // expected-warning {{unqualified call to std::forward}}
- // CHECK: {{^}} std::
+ (void)move(i); // expected-warning {{unqualified call to std::move}}
+ // CHECK: {{^}} (void)std::move
+ (void)forward(i); // expected-warning {{unqualified call to std::forward}}
+ // CHECK: {{^}} (void)std::forward
}
diff --git a/clang/test/SemaCXX/unqualified-std-call.cpp b/clang/test/SemaCXX/unqualified-std-call.cpp
index fa66ae9f8e321..0c78c26d063a9 100644
--- a/clang/test/SemaCXX/unqualified-std-call.cpp
+++ b/clang/test/SemaCXX/unqualified-std-call.cpp
@@ -1,17 +1,17 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wall -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wall -std=c++11 %s -Wno-unused-value
namespace std {
template <typename T>
void dummy(T &&) {}
template <typename T>
-void move(T &&) {}
+T &&move(T &&x) { return x; }
template <typename T, typename U>
void move(T &&, U &&) {}
inline namespace __1 {
template <typename T>
-void forward(T &) {}
+T &forward(T &x) { return x; }
} // namespace __1
struct foo {};
diff --git a/clang/test/SemaCXX/warn-consumed-analysis.cpp b/clang/test/SemaCXX/warn-consumed-analysis.cpp
index b4dddb6763724..8e09779e64404 100644
--- a/clang/test/SemaCXX/warn-consumed-analysis.cpp
+++ b/clang/test/SemaCXX/warn-consumed-analysis.cpp
@@ -953,12 +953,12 @@ void test6() {
namespace std {
void move();
template<class T>
- void move(T&&);
+ T &&move(T&);
namespace __1 {
void move();
template<class T>
- void move(T&&);
+ T &&move(T&);
}
}
@@ -971,7 +971,7 @@ namespace PR18260 {
void test() {
x.move();
std::move();
- std::move(x);
+ std::move(x); // expected-warning {{ignoring return value}}
std::__1::move();
std::__1::move(x);
}
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index a4410d330d393..e6360d88a893d 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -1444,7 +1444,7 @@ TEST(ExprMutationAnalyzerTest, UnevaluatedContext) {
TEST(ExprMutationAnalyzerTest, ReproduceFailureMinimal) {
const std::string Reproducer =
"namespace std {"
- "template <class T> T forward(T & A) { return static_cast<T&&>(A); }"
+ "template <class T> T &forward(T &A) { return static_cast<T&&>(A); }"
"template <class T> struct __bind {"
" T f;"
" template <class V> __bind(T v, V &&) : f(forward(v)) {}"
diff --git a/libcxx/test/std/utilities/utility/forward/forward.fail.cpp b/libcxx/test/std/utilities/utility/forward/forward.fail.cpp
index 4d7790a751481..f913b835c2248 100644
--- a/libcxx/test/std/utilities/utility/forward/forward.fail.cpp
+++ b/libcxx/test/std/utilities/utility/forward/forward.fail.cpp
@@ -22,7 +22,7 @@ const A csource() {return A();}
int main(int, char**)
{
{
- std::forward<A&>(source()); // expected-note {{requested here}}
+ (void)std::forward<A&>(source()); // expected-note {{requested here}}
// expected-error-re@*:* 1 {{static_assert failed{{.*}} "cannot forward an rvalue as an lvalue"}}
}
{
More information about the libcxx-commits
mailing list