.. 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
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
^^^^^^^^^^^^^^^^^^^^^
// 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.
// 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)
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)
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")
/// 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;
}
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;
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;
"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;
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);
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.
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: {
#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"
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);
/* 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);
/*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:
///
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;
}
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 =
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 {
}
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);
}
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(),
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:
}
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()))
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())
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();
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 differ 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:
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,
// 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));
}
}
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;
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();
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);
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();
}
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:
"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;
}
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)) {
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;
}
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;
}
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,
const char *c;
std::string s;
c = s.c_str();
- addressof(s);
+ (void)addressof(s);
consume(c); // no-warning
}
A a;
if (i == 1) { // peaceful-note 2 {{'i' is not equal to 1}}
// peaceful-note@-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@-1 2 {{Taking false branch}}
// 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
}
--- /dev/null
+// 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));
+}
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
// 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@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@ABU0@@Z" to i8*) }, section ".xdata", comdat
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() {
--- /dev/null
+// 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);
--- /dev/null
+// 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@-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@-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>'}}
--- /dev/null
+// 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);
+}
namespace std {
-void move(auto &&a) {}
+int &&move(auto &&a) { return a; }
-void forward(auto &a) {}
+int &&forward(auto &a) { return a; }
} // 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
}
-// 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 {};
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&);
}
}
void test() {
x.move();
std::move();
- std::move(x);
+ std::move(x); // expected-warning {{ignoring return value}}
std::__1::move();
std::__1::move(x);
}
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)) {}"
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"}}
}
{