forked from OSchip/llvm-project
Revert "[clang] NRVO: Improvements and handling of more cases."
This change caused build errors related to move-only __block variables, see discussion on https://reviews.llvm.org/D99696 > This expands NRVO propagation for more cases: > > Parse analysis improvement: > * Lambdas and Blocks with dependent return type can have their variables > marked as NRVO Candidates. > > Variable instantiation improvements: > * Fixes crash when instantiating NRVO variables in Blocks. > * Functions, Lambdas, and Blocks which have auto return type have their > variables' NRVO status propagated. For Blocks with non-auto return type, > as a limitation, this propagation does not consider the actual return > type. > > This also implements exclusion of VarDecls which are references to > dependent types. > > Signed-off-by: Matheus Izvekov <mizvekov@gmail.com> > > Reviewed By: Quuxplusone > > Differential Revision: https://reviews.llvm.org/D99696 This also reverts the follow-on change which was hard to tease apart form the one above: > "[clang] Implement P2266 Simpler implicit move" > > This Implements [[http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2266r1.html|P2266 Simpler implicit move]]. > > Signed-off-by: Matheus Izvekov <mizvekov@gmail.com> > > Reviewed By: Quuxplusone > > Differential Revision: https://reviews.llvm.org/D99005 This reverts commits1e50c3d785
andbf20631782
.
This commit is contained in:
parent
bfd451a0ca
commit
c60dd3b262
|
@ -3455,6 +3455,12 @@ public:
|
||||||
bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType);
|
bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType);
|
||||||
bool isSameOrCompatibleFunctionType(CanQualType Param, CanQualType Arg);
|
bool isSameOrCompatibleFunctionType(CanQualType Param, CanQualType Arg);
|
||||||
|
|
||||||
|
ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
|
||||||
|
const VarDecl *NRVOCandidate,
|
||||||
|
QualType ResultType,
|
||||||
|
Expr *Value,
|
||||||
|
bool AllowNRVO = true);
|
||||||
|
|
||||||
bool CanPerformAggregateInitializationForOverloadResolution(
|
bool CanPerformAggregateInitializationForOverloadResolution(
|
||||||
const InitializedEntity &Entity, InitListExpr *From);
|
const InitializedEntity &Entity, InitListExpr *From);
|
||||||
|
|
||||||
|
@ -4754,30 +4760,28 @@ public:
|
||||||
SourceLocation Loc,
|
SourceLocation Loc,
|
||||||
unsigned NumParams);
|
unsigned NumParams);
|
||||||
|
|
||||||
struct NamedReturnInfo {
|
enum CopyElisionSemanticsKind {
|
||||||
const VarDecl *Candidate;
|
CES_Strict = 0,
|
||||||
|
CES_AllowParameters = 1,
|
||||||
enum Status : uint8_t { None, MoveEligible, MoveEligibleAndCopyElidable };
|
CES_AllowDifferentTypes = 2,
|
||||||
Status S;
|
CES_AllowExceptionVariables = 4,
|
||||||
|
CES_AllowRValueReferenceType = 8,
|
||||||
bool isMoveEligible() const { return S != None; };
|
CES_ImplicitlyMovableCXX11CXX14CXX17 =
|
||||||
bool isCopyElidable() const { return S == MoveEligibleAndCopyElidable; }
|
(CES_AllowParameters | CES_AllowDifferentTypes),
|
||||||
|
CES_ImplicitlyMovableCXX20 =
|
||||||
|
(CES_AllowParameters | CES_AllowDifferentTypes |
|
||||||
|
CES_AllowExceptionVariables | CES_AllowRValueReferenceType),
|
||||||
};
|
};
|
||||||
NamedReturnInfo getNamedReturnInfo(Expr *&E, bool ForceCXX2b = false);
|
|
||||||
NamedReturnInfo getNamedReturnInfo(const VarDecl *VD,
|
|
||||||
bool ForceCXX20 = false);
|
|
||||||
const VarDecl *getCopyElisionCandidate(NamedReturnInfo &Info,
|
|
||||||
QualType ReturnType);
|
|
||||||
|
|
||||||
ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
|
VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E,
|
||||||
const NamedReturnInfo &NRInfo,
|
CopyElisionSemanticsKind CESK);
|
||||||
Expr *Value);
|
bool isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
|
||||||
|
CopyElisionSemanticsKind CESK);
|
||||||
|
|
||||||
StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
|
StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
|
||||||
Scope *CurScope);
|
Scope *CurScope);
|
||||||
StmtResult BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
|
StmtResult BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
|
||||||
StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
|
StmtResult ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
|
||||||
NamedReturnInfo &NRInfo);
|
|
||||||
|
|
||||||
StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
|
StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple,
|
||||||
bool IsVolatile, unsigned NumOutputs,
|
bool IsVolatile, unsigned NumOutputs,
|
||||||
|
|
|
@ -1962,10 +1962,9 @@ static void checkEscapingByref(VarDecl *VD, Sema &S) {
|
||||||
SourceLocation Loc = VD->getLocation();
|
SourceLocation Loc = VD->getLocation();
|
||||||
Expr *VarRef =
|
Expr *VarRef =
|
||||||
new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc);
|
new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc);
|
||||||
ExprResult Result = S.PerformCopyInitialization(
|
ExprResult Result = S.PerformMoveOrCopyInitialization(
|
||||||
InitializedEntity::InitializeBlock(Loc, T, false), SourceLocation(),
|
InitializedEntity::InitializeBlock(Loc, T, false), VD, VD->getType(),
|
||||||
VarRef);
|
VarRef, /*AllowNRVO=*/true);
|
||||||
|
|
||||||
if (!Result.isInvalid()) {
|
if (!Result.isInvalid()) {
|
||||||
Result = S.MaybeCreateExprWithCleanups(Result);
|
Result = S.MaybeCreateExprWithCleanups(Result);
|
||||||
Expr *Init = Result.getAs<Expr>();
|
Expr *Init = Result.getAs<Expr>();
|
||||||
|
|
|
@ -994,10 +994,26 @@ StmtResult Sema::BuildCoreturnStmt(SourceLocation Loc, Expr *E,
|
||||||
E = R.get();
|
E = R.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move the return value if we can
|
||||||
|
if (E) {
|
||||||
|
const VarDecl *NRVOCandidate = this->getCopyElisionCandidate(
|
||||||
|
E->getType(), E, CES_ImplicitlyMovableCXX20);
|
||||||
|
if (NRVOCandidate) {
|
||||||
|
InitializedEntity Entity =
|
||||||
|
InitializedEntity::InitializeResult(Loc, E->getType(), NRVOCandidate);
|
||||||
|
ExprResult MoveResult = this->PerformMoveOrCopyInitialization(
|
||||||
|
Entity, NRVOCandidate, E->getType(), E);
|
||||||
|
if (MoveResult.get())
|
||||||
|
E = MoveResult.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: If the operand is a reference to a variable that's about to go out
|
||||||
|
// of scope, we should treat the operand as an xvalue for this overload
|
||||||
|
// resolution.
|
||||||
VarDecl *Promise = FSI->CoroutinePromise;
|
VarDecl *Promise = FSI->CoroutinePromise;
|
||||||
ExprResult PC;
|
ExprResult PC;
|
||||||
if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) {
|
if (E && (isa<InitListExpr>(E) || !E->getType()->isVoidType())) {
|
||||||
getNamedReturnInfo(E, /*ForceCXX2b=*/true);
|
|
||||||
PC = buildPromiseCall(*this, Promise, Loc, "return_value", E);
|
PC = buildPromiseCall(*this, Promise, Loc, "return_value", E);
|
||||||
} else {
|
} else {
|
||||||
E = MakeFullDiscardedValueExpr(E).get();
|
E = MakeFullDiscardedValueExpr(E).get();
|
||||||
|
@ -1554,7 +1570,7 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() {
|
||||||
// Trigger a nice error message.
|
// Trigger a nice error message.
|
||||||
InitializedEntity Entity =
|
InitializedEntity Entity =
|
||||||
InitializedEntity::InitializeResult(Loc, FnRetType, false);
|
InitializedEntity::InitializeResult(Loc, FnRetType, false);
|
||||||
S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue);
|
S.PerformMoveOrCopyInitialization(Entity, nullptr, FnRetType, ReturnValue);
|
||||||
noteMemberDeclaredHere(S, ReturnValue, Fn);
|
noteMemberDeclaredHere(S, ReturnValue, Fn);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1570,8 +1586,8 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl);
|
InitializedEntity Entity = InitializedEntity::InitializeVariable(GroDecl);
|
||||||
ExprResult Res =
|
ExprResult Res = S.PerformMoveOrCopyInitialization(Entity, nullptr, GroType,
|
||||||
S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue);
|
this->ReturnValue);
|
||||||
if (Res.isInvalid())
|
if (Res.isInvalid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -854,6 +854,10 @@ ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex,
|
||||||
Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw";
|
Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw";
|
||||||
|
|
||||||
if (Ex && !Ex->isTypeDependent()) {
|
if (Ex && !Ex->isTypeDependent()) {
|
||||||
|
QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType());
|
||||||
|
if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex))
|
||||||
|
return ExprError();
|
||||||
|
|
||||||
// Initialize the exception result. This implicitly weeds out
|
// Initialize the exception result. This implicitly weeds out
|
||||||
// abstract types or types with inaccessible copy constructors.
|
// abstract types or types with inaccessible copy constructors.
|
||||||
|
|
||||||
|
@ -869,17 +873,15 @@ ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex,
|
||||||
// operation from the operand to the exception object (15.1) can be
|
// operation from the operand to the exception object (15.1) can be
|
||||||
// omitted by constructing the automatic object directly into the
|
// omitted by constructing the automatic object directly into the
|
||||||
// exception object
|
// exception object
|
||||||
NamedReturnInfo NRInfo =
|
const VarDecl *NRVOVariable = nullptr;
|
||||||
IsThrownVarInScope ? getNamedReturnInfo(Ex) : NamedReturnInfo();
|
if (IsThrownVarInScope)
|
||||||
|
NRVOVariable = getCopyElisionCandidate(QualType(), Ex, CES_Strict);
|
||||||
QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType());
|
|
||||||
if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex))
|
|
||||||
return ExprError();
|
|
||||||
|
|
||||||
InitializedEntity Entity = InitializedEntity::InitializeException(
|
InitializedEntity Entity = InitializedEntity::InitializeException(
|
||||||
OpLoc, ExceptionObjectTy,
|
OpLoc, ExceptionObjectTy,
|
||||||
/*NRVO=*/NRInfo.isCopyElidable());
|
/*NRVO=*/NRVOVariable != nullptr);
|
||||||
ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, Ex);
|
ExprResult Res = PerformMoveOrCopyInitialization(
|
||||||
|
Entity, NRVOVariable, QualType(), Ex, IsThrownVarInScope);
|
||||||
if (Res.isInvalid())
|
if (Res.isInvalid())
|
||||||
return ExprError();
|
return ExprError();
|
||||||
Ex = Res.get();
|
Ex = Res.get();
|
||||||
|
|
|
@ -3307,161 +3307,99 @@ Sema::ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope) {
|
||||||
return new (Context) BreakStmt(BreakLoc);
|
return new (Context) BreakStmt(BreakLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine whether the given expression might be move-eligible or
|
/// Determine whether the given expression is a candidate for
|
||||||
/// copy-elidable in either a (co_)return statement or throw expression,
|
/// copy elision in either a return statement or a throw expression.
|
||||||
/// without considering function return type, if applicable.
|
|
||||||
///
|
///
|
||||||
/// \param E The expression being returned from the function or block,
|
/// \param ReturnType If we're determining the copy elision candidate for
|
||||||
/// being thrown, or being co_returned from a coroutine. This expression
|
/// a return statement, this is the return type of the function. If we're
|
||||||
/// might be modified by the implementation.
|
/// determining the copy elision candidate for a throw expression, this will
|
||||||
|
/// be a NULL type.
|
||||||
///
|
///
|
||||||
/// \param ForceCXX2b Overrides detection of current language mode
|
/// \param E The expression being returned from the function or block, or
|
||||||
/// and uses the rules for C++2b.
|
/// being thrown.
|
||||||
///
|
///
|
||||||
/// \returns An aggregate which contains the Candidate and isMoveEligible
|
/// \param CESK Whether we allow function parameters or
|
||||||
/// and isCopyElidable methods. If Candidate is non-null, it means
|
/// id-expressions that could be moved out of the function to be considered NRVO
|
||||||
/// isMoveEligible() would be true under the most permissive language standard.
|
/// candidates. C++ prohibits these for NRVO itself, but we re-use this logic to
|
||||||
Sema::NamedReturnInfo Sema::getNamedReturnInfo(Expr *&E, bool ForceCXX2b) {
|
/// determine whether we should try to move as part of a return or throw (which
|
||||||
if (!E)
|
/// does allow function parameters).
|
||||||
return NamedReturnInfo();
|
///
|
||||||
|
/// \returns The NRVO candidate variable, if the return statement may use the
|
||||||
|
/// NRVO, or NULL if there is no such candidate.
|
||||||
|
VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType, Expr *E,
|
||||||
|
CopyElisionSemanticsKind CESK) {
|
||||||
// - in a return statement in a function [where] ...
|
// - in a return statement in a function [where] ...
|
||||||
// ... the expression is the name of a non-volatile automatic object ...
|
// ... the expression is the name of a non-volatile automatic object ...
|
||||||
const auto *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens());
|
DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens());
|
||||||
if (!DR || DR->refersToEnclosingVariableOrCapture())
|
if (!DR || DR->refersToEnclosingVariableOrCapture())
|
||||||
return NamedReturnInfo();
|
return nullptr;
|
||||||
const auto *VD = dyn_cast<VarDecl>(DR->getDecl());
|
VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
|
||||||
if (!VD)
|
if (!VD)
|
||||||
return NamedReturnInfo();
|
return nullptr;
|
||||||
NamedReturnInfo Res = getNamedReturnInfo(VD, /*ForceCXX20=*/ForceCXX2b);
|
|
||||||
if (Res.Candidate && !E->isXValue() &&
|
if (isCopyElisionCandidate(ReturnType, VD, CESK))
|
||||||
(ForceCXX2b || getLangOpts().CPlusPlus2b)) {
|
return VD;
|
||||||
E = ImplicitCastExpr::Create(Context, VD->getType().getNonReferenceType(),
|
return nullptr;
|
||||||
CK_NoOp, E, nullptr, VK_XValue,
|
|
||||||
FPOptionsOverride());
|
|
||||||
}
|
|
||||||
return Res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the status in the given NamedReturnInfo object to disallow
|
bool Sema::isCopyElisionCandidate(QualType ReturnType, const VarDecl *VD,
|
||||||
/// copy elision, and optionally also implicit move.
|
CopyElisionSemanticsKind CESK) {
|
||||||
///
|
QualType VDType = VD->getType();
|
||||||
/// \param Info The NamedReturnInfo object to update.
|
|
||||||
///
|
|
||||||
/// \param CanMove If true, disallow only copy elision.
|
|
||||||
/// If false, also disallow implcit move.
|
|
||||||
static void disallowNRVO(Sema::NamedReturnInfo &Info, bool CanMove) {
|
|
||||||
Info.S = std::min(Info.S, CanMove ? Sema::NamedReturnInfo::MoveEligible
|
|
||||||
: Sema::NamedReturnInfo::None);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determine whether the given NRVO candidate variable is move-eligible or
|
|
||||||
/// copy-elidable, without considering function return type.
|
|
||||||
///
|
|
||||||
/// \param VD The NRVO candidate variable.
|
|
||||||
///
|
|
||||||
/// \param ForceCXX20 Overrides detection of current language mode
|
|
||||||
/// and uses the rules for C++20.
|
|
||||||
///
|
|
||||||
/// \returns An aggregate which contains the Candidate and isMoveEligible
|
|
||||||
/// and isCopyElidable methods. If Candidate is non-null, it means
|
|
||||||
/// isMoveEligible() would be true under the most permissive language standard.
|
|
||||||
Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD,
|
|
||||||
bool ForceCXX20) {
|
|
||||||
bool hasCXX11 = getLangOpts().CPlusPlus11 || ForceCXX20;
|
|
||||||
bool hasCXX20 = getLangOpts().CPlusPlus20 || ForceCXX20;
|
|
||||||
NamedReturnInfo Info{VD, NamedReturnInfo::MoveEligibleAndCopyElidable};
|
|
||||||
|
|
||||||
// C++20 [class.copy.elision]p3:
|
|
||||||
// - in a return statement in a function with ...
|
// - in a return statement in a function with ...
|
||||||
// (other than a function ... parameter)
|
// ... a class return type ...
|
||||||
if (VD->getKind() == Decl::ParmVar)
|
if (!ReturnType.isNull() && !ReturnType->isDependentType()) {
|
||||||
disallowNRVO(Info, hasCXX11);
|
if (!ReturnType->isRecordType())
|
||||||
else if (VD->getKind() != Decl::Var)
|
return false;
|
||||||
return NamedReturnInfo();
|
// ... the same cv-unqualified type as the function return type ...
|
||||||
|
// When considering moving this expression out, allow dissimilar types.
|
||||||
|
if (!(CESK & CES_AllowDifferentTypes) && !VDType->isDependentType() &&
|
||||||
|
!Context.hasSameUnqualifiedType(ReturnType, VDType))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// (other than ... a catch-clause parameter)
|
// ...object (other than a function or catch-clause parameter)...
|
||||||
if (VD->isExceptionVariable())
|
if (VD->getKind() != Decl::Var &&
|
||||||
disallowNRVO(Info, hasCXX20);
|
!((CESK & CES_AllowParameters) && VD->getKind() == Decl::ParmVar))
|
||||||
|
return false;
|
||||||
|
if (!(CESK & CES_AllowExceptionVariables) && VD->isExceptionVariable())
|
||||||
|
return false;
|
||||||
|
|
||||||
// ...automatic...
|
// ...automatic...
|
||||||
if (!VD->hasLocalStorage())
|
if (!VD->hasLocalStorage()) return false;
|
||||||
return NamedReturnInfo();
|
|
||||||
|
|
||||||
// We don't want to implicitly move out of a __block variable during a return
|
// Return false if VD is a __block variable. We don't want to implicitly move
|
||||||
// because we cannot assume the variable will no longer be used.
|
// out of a __block variable during a return because we cannot assume the
|
||||||
|
// variable will no longer be used.
|
||||||
if (VD->hasAttr<BlocksAttr>())
|
if (VD->hasAttr<BlocksAttr>())
|
||||||
return NamedReturnInfo();
|
return false;
|
||||||
|
|
||||||
QualType VDType = VD->getType();
|
|
||||||
if (VDType->isObjectType()) {
|
if (VDType->isObjectType()) {
|
||||||
// C++17 [class.copy.elision]p3:
|
// C++17 [class.copy.elision]p3:
|
||||||
// ...non-volatile automatic object...
|
// ...non-volatile automatic object...
|
||||||
if (VDType.isVolatileQualified())
|
if (VDType.isVolatileQualified())
|
||||||
return NamedReturnInfo();
|
return false;
|
||||||
} else if (VDType->isRValueReferenceType()) {
|
} else if (VDType->isRValueReferenceType()) {
|
||||||
// C++20 [class.copy.elision]p3:
|
// C++20 [class.copy.elision]p3:
|
||||||
// ...either a non-volatile object or an rvalue reference to a non-volatile
|
// ...either a non-volatile object or an rvalue reference to a non-volatile object type...
|
||||||
// object type...
|
if (!(CESK & CES_AllowRValueReferenceType))
|
||||||
|
return false;
|
||||||
QualType VDReferencedType = VDType.getNonReferenceType();
|
QualType VDReferencedType = VDType.getNonReferenceType();
|
||||||
if (VDReferencedType.isVolatileQualified() ||
|
if (VDReferencedType.isVolatileQualified() || !VDReferencedType->isObjectType())
|
||||||
!VDReferencedType->isObjectType())
|
return false;
|
||||||
return NamedReturnInfo();
|
|
||||||
disallowNRVO(Info, hasCXX20);
|
|
||||||
} else {
|
} else {
|
||||||
return NamedReturnInfo();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CESK & CES_AllowDifferentTypes)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Variables with higher required alignment than their type's ABI
|
// Variables with higher required alignment than their type's ABI
|
||||||
// alignment cannot use NRVO.
|
// alignment cannot use NRVO.
|
||||||
if (!VDType->isDependentType() && VD->hasAttr<AlignedAttr>() &&
|
if (!VDType->isDependentType() && VD->hasAttr<AlignedAttr>() &&
|
||||||
Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType))
|
Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType))
|
||||||
disallowNRVO(Info, hasCXX11);
|
return false;
|
||||||
|
|
||||||
return Info;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates given NamedReturnInfo's move-eligible and
|
|
||||||
/// copy-elidable statuses, considering the function
|
|
||||||
/// return type criteria as applicable to return statements.
|
|
||||||
///
|
|
||||||
/// \param Info The NamedReturnInfo object to update.
|
|
||||||
///
|
|
||||||
/// \param ReturnType This is the return type of the function.
|
|
||||||
/// \returns The copy elision candidate, in case the initial return expression
|
|
||||||
/// was copy elidable, or nullptr otherwise.
|
|
||||||
const VarDecl *Sema::getCopyElisionCandidate(NamedReturnInfo &Info,
|
|
||||||
QualType ReturnType) {
|
|
||||||
if (!Info.Candidate)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
auto invalidNRVO = [&] {
|
|
||||||
Info = NamedReturnInfo();
|
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we got a non-deduced auto ReturnType, we are in a dependent context and
|
|
||||||
// there is no point in allowing copy elision since we won't have it deduced
|
|
||||||
// by the point the VardDecl is instantiated, which is the last chance we have
|
|
||||||
// of deciding if the candidate is really copy elidable.
|
|
||||||
if ((ReturnType->getTypeClass() == Type::TypeClass::Auto &&
|
|
||||||
ReturnType->isCanonicalUnqualified()) ||
|
|
||||||
ReturnType->isSpecificBuiltinType(BuiltinType::Dependent))
|
|
||||||
return invalidNRVO();
|
|
||||||
|
|
||||||
if (!ReturnType->isDependentType()) {
|
|
||||||
// - in a return statement in a function with ...
|
|
||||||
// ... a class return type ...
|
|
||||||
if (!ReturnType->isRecordType())
|
|
||||||
return invalidNRVO();
|
|
||||||
|
|
||||||
QualType VDType = Info.Candidate->getType();
|
|
||||||
// ... the same cv-unqualified type as the function return type ...
|
|
||||||
// When considering moving this expression out, allow dissimilar types.
|
|
||||||
if (!VDType->isDependentType() &&
|
|
||||||
!Context.hasSameUnqualifiedType(ReturnType, VDType))
|
|
||||||
disallowNRVO(Info, getLangOpts().CPlusPlus11);
|
|
||||||
}
|
|
||||||
return Info.isCopyElidable() ? Info.Candidate : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to perform the initialization of a potentially-movable value,
|
/// Try to perform the initialization of a potentially-movable value,
|
||||||
|
@ -3486,7 +3424,8 @@ const VarDecl *Sema::getCopyElisionCandidate(NamedReturnInfo &Info,
|
||||||
/// the selected constructor/operator doesn't match the additional criteria, we
|
/// the selected constructor/operator doesn't match the additional criteria, we
|
||||||
/// need to do the second overload resolution.
|
/// need to do the second overload resolution.
|
||||||
static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
|
static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
|
||||||
const VarDecl *NRVOCandidate, Expr *&Value,
|
const VarDecl *NRVOCandidate,
|
||||||
|
QualType ResultType, Expr *&Value,
|
||||||
bool ConvertingConstructorsOnly,
|
bool ConvertingConstructorsOnly,
|
||||||
bool IsDiagnosticsCheck, ExprResult &Res) {
|
bool IsDiagnosticsCheck, ExprResult &Res) {
|
||||||
ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(),
|
ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(),
|
||||||
|
@ -3569,41 +3508,63 @@ static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity,
|
||||||
/// This routine implements C++20 [class.copy.elision]p3, which attempts to
|
/// This routine implements C++20 [class.copy.elision]p3, which attempts to
|
||||||
/// treat returned lvalues as rvalues in certain cases (to prefer move
|
/// treat returned lvalues as rvalues in certain cases (to prefer move
|
||||||
/// construction), then falls back to treating them as lvalues if that failed.
|
/// construction), then falls back to treating them as lvalues if that failed.
|
||||||
ExprResult
|
ExprResult Sema::PerformMoveOrCopyInitialization(
|
||||||
Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
|
const InitializedEntity &Entity, const VarDecl *NRVOCandidate,
|
||||||
const NamedReturnInfo &NRInfo,
|
QualType ResultType, Expr *Value, bool AllowNRVO) {
|
||||||
Expr *Value) {
|
ExprResult Res = ExprError();
|
||||||
|
bool NeedSecondOverloadResolution = true;
|
||||||
|
|
||||||
if (NRInfo.Candidate && !getLangOpts().CPlusPlus2b) {
|
if (AllowNRVO) {
|
||||||
if (NRInfo.isMoveEligible()) {
|
CopyElisionSemanticsKind CESK = CES_Strict;
|
||||||
ExprResult Res;
|
if (getLangOpts().CPlusPlus20) {
|
||||||
if (!TryMoveInitialization(*this, Entity, NRInfo.Candidate, Value,
|
CESK = CES_ImplicitlyMovableCXX20;
|
||||||
!getLangOpts().CPlusPlus20, false, Res))
|
} else if (getLangOpts().CPlusPlus11) {
|
||||||
return Res;
|
CESK = CES_ImplicitlyMovableCXX11CXX14CXX17;
|
||||||
}
|
}
|
||||||
if (!getDiagnostics().isIgnored(diag::warn_return_std_move,
|
|
||||||
|
if (!NRVOCandidate) {
|
||||||
|
NRVOCandidate = getCopyElisionCandidate(ResultType, Value, CESK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NRVOCandidate) {
|
||||||
|
NeedSecondOverloadResolution =
|
||||||
|
TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value,
|
||||||
|
!getLangOpts().CPlusPlus20, false, Res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getLangOpts().CPlusPlus20 && NeedSecondOverloadResolution &&
|
||||||
|
!getDiagnostics().isIgnored(diag::warn_return_std_move,
|
||||||
Value->getExprLoc())) {
|
Value->getExprLoc())) {
|
||||||
QualType QT = NRInfo.Candidate->getType();
|
const VarDecl *FakeNRVOCandidate = getCopyElisionCandidate(
|
||||||
if (QT.getNonReferenceType().getUnqualifiedType().isTriviallyCopyableType(
|
QualType(), Value, CES_ImplicitlyMovableCXX20);
|
||||||
Context)) {
|
if (FakeNRVOCandidate) {
|
||||||
// Adding 'std::move' around a trivially copyable variable is probably
|
QualType QT = FakeNRVOCandidate->getType();
|
||||||
// pointless. Don't suggest it.
|
if (QT->isLValueReferenceType()) {
|
||||||
} else {
|
// Adding 'std::move' around an lvalue reference variable's name is
|
||||||
ExprResult FakeRes = ExprError();
|
// dangerous. Don't suggest it.
|
||||||
Expr *FakeValue = Value;
|
} else if (QT.getNonReferenceType()
|
||||||
TryMoveInitialization(*this, Entity, NRInfo.Candidate, FakeValue, false,
|
.getUnqualifiedType()
|
||||||
true, FakeRes);
|
.isTriviallyCopyableType(Context)) {
|
||||||
if (!FakeRes.isInvalid()) {
|
// Adding 'std::move' around a trivially copyable variable is probably
|
||||||
bool IsThrow = (Entity.getKind() == InitializedEntity::EK_Exception);
|
// pointless. Don't suggest it.
|
||||||
SmallString<32> Str;
|
} else {
|
||||||
Str += "std::move(";
|
ExprResult FakeRes = ExprError();
|
||||||
Str += NRInfo.Candidate->getDeclName().getAsString();
|
Expr *FakeValue = Value;
|
||||||
Str += ")";
|
TryMoveInitialization(*this, Entity, FakeNRVOCandidate, ResultType,
|
||||||
Diag(Value->getExprLoc(), diag::warn_return_std_move)
|
FakeValue, false, true, FakeRes);
|
||||||
<< Value->getSourceRange() << NRInfo.Candidate->getDeclName()
|
if (!FakeRes.isInvalid()) {
|
||||||
<< IsThrow;
|
bool IsThrow =
|
||||||
Diag(Value->getExprLoc(), diag::note_add_std_move)
|
(Entity.getKind() == InitializedEntity::EK_Exception);
|
||||||
<< FixItHint::CreateReplacement(Value->getSourceRange(), Str);
|
SmallString<32> Str;
|
||||||
|
Str += "std::move(";
|
||||||
|
Str += FakeNRVOCandidate->getDeclName().getAsString();
|
||||||
|
Str += ")";
|
||||||
|
Diag(Value->getExprLoc(), diag::warn_return_std_move)
|
||||||
|
<< Value->getSourceRange()
|
||||||
|
<< FakeNRVOCandidate->getDeclName() << IsThrow;
|
||||||
|
Diag(Value->getExprLoc(), diag::note_add_std_move)
|
||||||
|
<< FixItHint::CreateReplacement(Value->getSourceRange(), Str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3612,7 +3573,10 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
|
||||||
// Either we didn't meet the criteria for treating an lvalue as an rvalue,
|
// Either we didn't meet the criteria for treating an lvalue as an rvalue,
|
||||||
// above, or overload resolution failed. Either way, we need to try
|
// above, or overload resolution failed. Either way, we need to try
|
||||||
// (again) now with the return value expression as written.
|
// (again) now with the return value expression as written.
|
||||||
return PerformCopyInitialization(Entity, SourceLocation(), Value);
|
if (NeedSecondOverloadResolution)
|
||||||
|
Res = PerformCopyInitialization(Entity, SourceLocation(), Value);
|
||||||
|
|
||||||
|
return Res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine whether the declared return type of the specified function
|
/// Determine whether the declared return type of the specified function
|
||||||
|
@ -3626,9 +3590,8 @@ static bool hasDeducedReturnType(FunctionDecl *FD) {
|
||||||
/// ActOnCapScopeReturnStmt - Utility routine to type-check return statements
|
/// ActOnCapScopeReturnStmt - Utility routine to type-check return statements
|
||||||
/// for capturing scopes.
|
/// for capturing scopes.
|
||||||
///
|
///
|
||||||
StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
|
StmtResult
|
||||||
Expr *RetValExp,
|
Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
|
||||||
NamedReturnInfo &NRInfo) {
|
|
||||||
// If this is the first return we've seen, infer the return type.
|
// If this is the first return we've seen, infer the return type.
|
||||||
// [expr.prim.lambda]p4 in C++11; block literals follow the same rules.
|
// [expr.prim.lambda]p4 in C++11; block literals follow the same rules.
|
||||||
CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction());
|
CapturingScopeInfo *CurCap = cast<CapturingScopeInfo>(getCurFunction());
|
||||||
|
@ -3707,7 +3670,7 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
|
||||||
if (CurCap->ReturnType.isNull())
|
if (CurCap->ReturnType.isNull())
|
||||||
CurCap->ReturnType = FnRetType;
|
CurCap->ReturnType = FnRetType;
|
||||||
}
|
}
|
||||||
const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType);
|
assert(!FnRetType.isNull());
|
||||||
|
|
||||||
if (auto *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) {
|
if (auto *CurBlock = dyn_cast<BlockScopeInfo>(CurCap)) {
|
||||||
if (CurBlock->FunctionType->castAs<FunctionType>()->getNoReturnAttr()) {
|
if (CurBlock->FunctionType->castAs<FunctionType>()->getNoReturnAttr()) {
|
||||||
|
@ -3730,6 +3693,7 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
|
||||||
// Otherwise, verify that this result type matches the previous one. We are
|
// Otherwise, verify that this result type matches the previous one. We are
|
||||||
// pickier with blocks than for normal functions because we don't have GCC
|
// pickier with blocks than for normal functions because we don't have GCC
|
||||||
// compatibility to worry about here.
|
// compatibility to worry about here.
|
||||||
|
const VarDecl *NRVOCandidate = nullptr;
|
||||||
if (FnRetType->isDependentType()) {
|
if (FnRetType->isDependentType()) {
|
||||||
// Delay processing for now. TODO: there are lots of dependent
|
// Delay processing for now. TODO: there are lots of dependent
|
||||||
// types we can conclusively prove aren't void.
|
// types we can conclusively prove aren't void.
|
||||||
|
@ -3757,15 +3721,20 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
|
||||||
|
|
||||||
// In C++ the return statement is handled via a copy initialization.
|
// In C++ the return statement is handled via a copy initialization.
|
||||||
// the C version of which boils down to CheckSingleAssignmentConstraints.
|
// the C version of which boils down to CheckSingleAssignmentConstraints.
|
||||||
InitializedEntity Entity = InitializedEntity::InitializeResult(
|
NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict);
|
||||||
ReturnLoc, FnRetType, NRVOCandidate != nullptr);
|
InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc,
|
||||||
ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp);
|
FnRetType,
|
||||||
|
NRVOCandidate != nullptr);
|
||||||
|
ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate,
|
||||||
|
FnRetType, RetValExp);
|
||||||
if (Res.isInvalid()) {
|
if (Res.isInvalid()) {
|
||||||
// FIXME: Cleanup temporaries here, anyway?
|
// FIXME: Cleanup temporaries here, anyway?
|
||||||
return StmtError();
|
return StmtError();
|
||||||
}
|
}
|
||||||
RetValExp = Res.get();
|
RetValExp = Res.get();
|
||||||
CheckReturnValExpr(RetValExp, FnRetType, ReturnLoc);
|
CheckReturnValExpr(RetValExp, FnRetType, ReturnLoc);
|
||||||
|
} else {
|
||||||
|
NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RetValExp) {
|
if (RetValExp) {
|
||||||
|
@ -3974,10 +3943,8 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
|
||||||
if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp))
|
if (RetValExp && DiagnoseUnexpandedParameterPack(RetValExp))
|
||||||
return StmtError();
|
return StmtError();
|
||||||
|
|
||||||
NamedReturnInfo NRInfo = getNamedReturnInfo(RetValExp);
|
|
||||||
|
|
||||||
if (isa<CapturingScopeInfo>(getCurFunction()))
|
if (isa<CapturingScopeInfo>(getCurFunction()))
|
||||||
return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp, NRInfo);
|
return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp);
|
||||||
|
|
||||||
QualType FnRetType;
|
QualType FnRetType;
|
||||||
QualType RelatedRetType;
|
QualType RelatedRetType;
|
||||||
|
@ -4049,7 +4016,6 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const VarDecl *NRVOCandidate = getCopyElisionCandidate(NRInfo, FnRetType);
|
|
||||||
|
|
||||||
bool HasDependentReturnType = FnRetType->isDependentType();
|
bool HasDependentReturnType = FnRetType->isDependentType();
|
||||||
|
|
||||||
|
@ -4156,6 +4122,8 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
|
||||||
/* NRVOCandidate=*/nullptr);
|
/* NRVOCandidate=*/nullptr);
|
||||||
} else {
|
} else {
|
||||||
assert(RetValExp || HasDependentReturnType);
|
assert(RetValExp || HasDependentReturnType);
|
||||||
|
const VarDecl *NRVOCandidate = nullptr;
|
||||||
|
|
||||||
QualType RetType = RelatedRetType.isNull() ? FnRetType : RelatedRetType;
|
QualType RetType = RelatedRetType.isNull() ? FnRetType : RelatedRetType;
|
||||||
|
|
||||||
// C99 6.8.6.4p3(136): The return statement is not an assignment. The
|
// C99 6.8.6.4p3(136): The return statement is not an assignment. The
|
||||||
|
@ -4164,12 +4132,15 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
|
||||||
|
|
||||||
// In C++ the return statement is handled via a copy initialization,
|
// In C++ the return statement is handled via a copy initialization,
|
||||||
// the C version of which boils down to CheckSingleAssignmentConstraints.
|
// the C version of which boils down to CheckSingleAssignmentConstraints.
|
||||||
|
if (RetValExp)
|
||||||
|
NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, CES_Strict);
|
||||||
if (!HasDependentReturnType && !RetValExp->isTypeDependent()) {
|
if (!HasDependentReturnType && !RetValExp->isTypeDependent()) {
|
||||||
// we have a non-void function with an expression, continue checking
|
// we have a non-void function with an expression, continue checking
|
||||||
InitializedEntity Entity = InitializedEntity::InitializeResult(
|
InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc,
|
||||||
ReturnLoc, RetType, NRVOCandidate != nullptr);
|
RetType,
|
||||||
ExprResult Res =
|
NRVOCandidate != nullptr);
|
||||||
PerformMoveOrCopyInitialization(Entity, NRInfo, RetValExp);
|
ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRVOCandidate,
|
||||||
|
RetType, RetValExp);
|
||||||
if (Res.isInvalid()) {
|
if (Res.isInvalid()) {
|
||||||
// FIXME: Clean up temporaries here anyway?
|
// FIXME: Clean up temporaries here anyway?
|
||||||
return StmtError();
|
return StmtError();
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include "clang/Basic/TargetInfo.h"
|
#include "clang/Basic/TargetInfo.h"
|
||||||
#include "clang/Sema/Initialization.h"
|
#include "clang/Sema/Initialization.h"
|
||||||
#include "clang/Sema/Lookup.h"
|
#include "clang/Sema/Lookup.h"
|
||||||
#include "clang/Sema/ScopeInfo.h"
|
|
||||||
#include "clang/Sema/SemaInternal.h"
|
#include "clang/Sema/SemaInternal.h"
|
||||||
#include "clang/Sema/Template.h"
|
#include "clang/Sema/Template.h"
|
||||||
#include "clang/Sema/TemplateInstCallback.h"
|
#include "clang/Sema/TemplateInstCallback.h"
|
||||||
|
@ -1086,30 +1085,11 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D,
|
||||||
|
|
||||||
SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, Owner,
|
SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, Owner,
|
||||||
StartingScope, InstantiatingVarTemplate);
|
StartingScope, InstantiatingVarTemplate);
|
||||||
if (D->isNRVOVariable()) {
|
|
||||||
QualType RT;
|
|
||||||
if (auto *F = dyn_cast<FunctionDecl>(DC))
|
|
||||||
RT = F->getReturnType();
|
|
||||||
else if (isa<BlockDecl>(DC))
|
|
||||||
RT = cast<FunctionType>(SemaRef.getCurBlock()->FunctionType)
|
|
||||||
->getReturnType();
|
|
||||||
else
|
|
||||||
llvm_unreachable("Unknown context type");
|
|
||||||
|
|
||||||
// This is the last chance we have of checking copy elision eligibility
|
if (D->isNRVOVariable()) {
|
||||||
// for functions in depdendent contexts. The sema actions for building
|
QualType ReturnType = cast<FunctionDecl>(DC)->getReturnType();
|
||||||
// the return statement during template instantiation will have no effect
|
if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict))
|
||||||
// regarding copy elision, since NRVO propagation runs on the scope exit
|
Var->setNRVOVariable(true);
|
||||||
// actions, and these are not run on instantiation.
|
|
||||||
// This might run through some VarDecls which were returned from non-taken
|
|
||||||
// 'if constexpr' branches, and these will end up being constructed on the
|
|
||||||
// return slot even if they will never be returned, as a sort of accidental
|
|
||||||
// 'optimization'. Notably, functions with 'auto' return types won't have it
|
|
||||||
// deduced by this point. Coupled with the limitation described
|
|
||||||
// previously, this makes it very hard to support copy elision for these.
|
|
||||||
Sema::NamedReturnInfo Info = SemaRef.getNamedReturnInfo(Var);
|
|
||||||
bool NRVO = SemaRef.getCopyElisionCandidate(Info, RT) != nullptr;
|
|
||||||
Var->setNRVOVariable(NRVO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Var->setImplicit(D->isImplicit());
|
Var->setImplicit(D->isImplicit());
|
||||||
|
|
|
@ -8899,10 +8899,6 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) {
|
||||||
if (E->isTypeDependent())
|
if (E->isTypeDependent())
|
||||||
return S.Context.DependentTy;
|
return S.Context.DependentTy;
|
||||||
|
|
||||||
Expr *IDExpr = E;
|
|
||||||
if (auto *ImplCastExpr = dyn_cast<ImplicitCastExpr>(E))
|
|
||||||
IDExpr = ImplCastExpr->getSubExpr();
|
|
||||||
|
|
||||||
// C++11 [dcl.type.simple]p4:
|
// C++11 [dcl.type.simple]p4:
|
||||||
// The type denoted by decltype(e) is defined as follows:
|
// The type denoted by decltype(e) is defined as follows:
|
||||||
|
|
||||||
|
@ -8913,7 +8909,7 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) {
|
||||||
// Note that this does not pick up the implicit 'const' for a template
|
// Note that this does not pick up the implicit 'const' for a template
|
||||||
// parameter object. This rule makes no difference before C++20 so we apply
|
// parameter object. This rule makes no difference before C++20 so we apply
|
||||||
// it unconditionally.
|
// it unconditionally.
|
||||||
if (const auto *SNTTPE = dyn_cast<SubstNonTypeTemplateParmExpr>(IDExpr))
|
if (const auto *SNTTPE = dyn_cast<SubstNonTypeTemplateParmExpr>(E))
|
||||||
return SNTTPE->getParameterType(S.Context);
|
return SNTTPE->getParameterType(S.Context);
|
||||||
|
|
||||||
// - if e is an unparenthesized id-expression or an unparenthesized class
|
// - if e is an unparenthesized id-expression or an unparenthesized class
|
||||||
|
@ -8922,22 +8918,21 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) {
|
||||||
// functions, the program is ill-formed;
|
// functions, the program is ill-formed;
|
||||||
//
|
//
|
||||||
// We apply the same rules for Objective-C ivar and property references.
|
// We apply the same rules for Objective-C ivar and property references.
|
||||||
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(IDExpr)) {
|
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
|
||||||
const ValueDecl *VD = DRE->getDecl();
|
const ValueDecl *VD = DRE->getDecl();
|
||||||
if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(VD))
|
if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(VD))
|
||||||
return TPO->getType().getUnqualifiedType();
|
return TPO->getType().getUnqualifiedType();
|
||||||
return VD->getType();
|
return VD->getType();
|
||||||
} else if (const MemberExpr *ME = dyn_cast<MemberExpr>(IDExpr)) {
|
} else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
|
||||||
if (const ValueDecl *VD = ME->getMemberDecl())
|
if (const ValueDecl *VD = ME->getMemberDecl())
|
||||||
if (isa<FieldDecl>(VD) || isa<VarDecl>(VD))
|
if (isa<FieldDecl>(VD) || isa<VarDecl>(VD))
|
||||||
return VD->getType();
|
return VD->getType();
|
||||||
} else if (const ObjCIvarRefExpr *IR = dyn_cast<ObjCIvarRefExpr>(IDExpr)) {
|
} else if (const ObjCIvarRefExpr *IR = dyn_cast<ObjCIvarRefExpr>(E)) {
|
||||||
return IR->getDecl()->getType();
|
return IR->getDecl()->getType();
|
||||||
} else if (const ObjCPropertyRefExpr *PR =
|
} else if (const ObjCPropertyRefExpr *PR = dyn_cast<ObjCPropertyRefExpr>(E)) {
|
||||||
dyn_cast<ObjCPropertyRefExpr>(IDExpr)) {
|
|
||||||
if (PR->isExplicitProperty())
|
if (PR->isExplicitProperty())
|
||||||
return PR->getExplicitProperty()->getType();
|
return PR->getExplicitProperty()->getType();
|
||||||
} else if (auto *PE = dyn_cast<PredefinedExpr>(IDExpr)) {
|
} else if (auto *PE = dyn_cast<PredefinedExpr>(E)) {
|
||||||
return PE->getType();
|
return PE->getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8950,8 +8945,8 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) {
|
||||||
// entity.
|
// entity.
|
||||||
using namespace sema;
|
using namespace sema;
|
||||||
if (S.getCurLambda()) {
|
if (S.getCurLambda()) {
|
||||||
if (isa<ParenExpr>(IDExpr)) {
|
if (isa<ParenExpr>(E)) {
|
||||||
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(IDExpr->IgnoreParens())) {
|
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
|
||||||
if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
|
if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
|
||||||
QualType T = S.getCapturedDeclRefType(Var, DRE->getLocation());
|
QualType T = S.getCapturedDeclRefType(Var, DRE->getLocation());
|
||||||
if (!T.isNull())
|
if (!T.isNull())
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b,cxx2b %s
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b %s
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_20,cxx20_2b %s
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected,cxx20_2b %s
|
||||||
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s
|
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17 %s
|
||||||
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s
|
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17 %s
|
||||||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17,cxx11_20 %s
|
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected,cxx11_17 %s
|
||||||
|
|
||||||
namespace test_delete_function {
|
namespace test_delete_function {
|
||||||
struct A1 {
|
struct A1 {
|
||||||
|
@ -409,10 +409,8 @@ Target t4() {
|
||||||
namespace test_simpler_implicit_move {
|
namespace test_simpler_implicit_move {
|
||||||
|
|
||||||
struct CopyOnly {
|
struct CopyOnly {
|
||||||
CopyOnly(); // cxx2b-note {{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
|
CopyOnly();
|
||||||
// cxx2b-note@-1 {{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
|
CopyOnly(CopyOnly &);
|
||||||
CopyOnly(CopyOnly &); // cxx2b-note {{candidate constructor not viable: expects an lvalue for 1st argument}}
|
|
||||||
// cxx2b-note@-1 {{candidate constructor not viable: expects an lvalue for 1st argument}}
|
|
||||||
};
|
};
|
||||||
struct MoveOnly {
|
struct MoveOnly {
|
||||||
MoveOnly();
|
MoveOnly();
|
||||||
|
@ -421,7 +419,7 @@ struct MoveOnly {
|
||||||
MoveOnly &&rref();
|
MoveOnly &&rref();
|
||||||
|
|
||||||
MoveOnly &&test1(MoveOnly &&w) {
|
MoveOnly &&test1(MoveOnly &&w) {
|
||||||
return w; // cxx11_20-error {{cannot bind to lvalue of type}}
|
return w; // expected-error {{cannot bind to lvalue of type}}
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyOnly test2(bool b) {
|
CopyOnly test2(bool b) {
|
||||||
|
@ -430,22 +428,22 @@ CopyOnly test2(bool b) {
|
||||||
if (b) {
|
if (b) {
|
||||||
return w1;
|
return w1;
|
||||||
} else {
|
} else {
|
||||||
return w2; // cxx2b-error {{no matching constructor for initialization}}
|
return w2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> T &&test3(T &&x) { return x; } // cxx11_20-error {{cannot bind to lvalue of type}}
|
template <class T> T &&test3(T &&x) { return x; } // expected-error {{cannot bind to lvalue of type}}
|
||||||
template MoveOnly& test3<MoveOnly&>(MoveOnly&);
|
template MoveOnly& test3<MoveOnly&>(MoveOnly&);
|
||||||
template MoveOnly &&test3<MoveOnly>(MoveOnly &&); // cxx11_20-note {{in instantiation of function template specialization}}
|
template MoveOnly&& test3<MoveOnly>(MoveOnly&&); // expected-note {{in instantiation of function template specialization}}
|
||||||
|
|
||||||
MoveOnly &&test4() {
|
MoveOnly &&test4() {
|
||||||
MoveOnly &&x = rref();
|
MoveOnly &&x = rref();
|
||||||
return x; // cxx11_20-error {{cannot bind to lvalue of type}}
|
return x; // expected-error {{cannot bind to lvalue of type}}
|
||||||
}
|
}
|
||||||
|
|
||||||
void test5() try {
|
void test5() try {
|
||||||
CopyOnly x;
|
CopyOnly x;
|
||||||
throw x; // cxx2b-error {{no matching constructor for initialization}}
|
throw x;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// RUN: %clang_cc1 -verify -std=c++2b -verify=expected,cxx2b %s
|
// RUN: %clang_cc1 -verify -std=c++2b -verify %s
|
||||||
// RUN: %clang_cc1 -verify -std=c++20 -verify=expected,cxx14_20 %s
|
// RUN: %clang_cc1 -verify -std=c++20 -verify %s
|
||||||
// RUN: %clang_cc1 -verify -std=c++14 -verify=expected,cxx14_20 %s
|
// RUN: %clang_cc1 -verify -std=c++14 -verify %s
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
template<typename T> struct initializer_list {
|
template<typename T> struct initializer_list {
|
||||||
|
@ -30,7 +30,7 @@ using Int = decltype(x3d);
|
||||||
auto x4a = (i);
|
auto x4a = (i);
|
||||||
decltype(auto) x4d = (i);
|
decltype(auto) x4d = (i);
|
||||||
using Int = decltype(x4a);
|
using Int = decltype(x4a);
|
||||||
using IntLRef = decltype(x4d); // cxx2b-note {{previous definition is here}}
|
using IntLRef = decltype(x4d);
|
||||||
|
|
||||||
auto x5a = f();
|
auto x5a = f();
|
||||||
decltype(auto) x5d = f();
|
decltype(auto) x5d = f();
|
||||||
|
@ -81,7 +81,7 @@ using Int = decltype(f2d(0));
|
||||||
auto f3a(int n) { return (n); }
|
auto f3a(int n) { return (n); }
|
||||||
decltype(auto) f3d(int n) { return (n); } // expected-warning {{reference to stack memory}}
|
decltype(auto) f3d(int n) { return (n); } // expected-warning {{reference to stack memory}}
|
||||||
using Int = decltype(f3a(0));
|
using Int = decltype(f3a(0));
|
||||||
using IntLRef = decltype(f3d(0)); // cxx2b-error {{type alias redefinition with different types ('decltype(f3d(0))' (aka 'int &&') vs 'decltype(x4d)' (aka 'int &'))}}
|
using IntLRef = decltype(f3d(0));
|
||||||
|
|
||||||
auto f4a(int n) { return f(); }
|
auto f4a(int n) { return f(); }
|
||||||
decltype(auto) f4d(int n) { return f(); }
|
decltype(auto) f4d(int n) { return f(); }
|
||||||
|
@ -91,7 +91,7 @@ using IntRRef = decltype(f4d(0));
|
||||||
auto f5aa(int n) { auto x = f(); return x; }
|
auto f5aa(int n) { auto x = f(); return x; }
|
||||||
auto f5ad(int n) { decltype(auto) x = f(); return x; }
|
auto f5ad(int n) { decltype(auto) x = f(); return x; }
|
||||||
decltype(auto) f5da(int n) { auto x = f(); return x; }
|
decltype(auto) f5da(int n) { auto x = f(); return x; }
|
||||||
decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // cxx14_20-error {{rvalue reference to type 'int' cannot bind to lvalue}}
|
decltype(auto) f5dd(int n) { decltype(auto) x = f(); return x; } // expected-error {{rvalue reference to type 'int' cannot bind to lvalue}}
|
||||||
using Int = decltype(f5aa(0));
|
using Int = decltype(f5aa(0));
|
||||||
using Int = decltype(f5ad(0));
|
using Int = decltype(f5ad(0));
|
||||||
using Int = decltype(f5da(0));
|
using Int = decltype(f5da(0));
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// RUN: %clang_cc1 -std=c++2b -verify=expected,cxx20_2b,cxx2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++2b -verify=expected,cxx20_2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
||||||
// RUN: %clang_cc1 -std=c++20 -verify=expected,cxx98_20,cxx20_2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++20 -verify=expected,cxx20_2b -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
||||||
// RUN: %clang_cc1 -std=c++17 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++17 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
||||||
// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
||||||
// RUN: %clang_cc1 -std=c++11 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++11 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
||||||
// RUN: %clang_cc1 -std=c++98 -verify=expected,cxx98_17,cxx98_20 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++98 -verify=expected,cxx98_17 -triple %itanium_abi_triple %s -fexceptions -fcxx-exceptions -pedantic-errors
|
||||||
|
|
||||||
namespace dr300 { // dr300: yes
|
namespace dr300 { // dr300: yes
|
||||||
template<typename R, typename A> void f(R (&)(A)) {}
|
template<typename R, typename A> void f(R (&)(A)) {}
|
||||||
|
@ -628,8 +628,7 @@ namespace dr349 { // dr349: no
|
||||||
struct A {
|
struct A {
|
||||||
template <class T> operator T ***() {
|
template <class T> operator T ***() {
|
||||||
int ***p = 0;
|
int ***p = 0;
|
||||||
return p; // cxx98_20-error {{cannot initialize return object of type 'const int ***' with an lvalue of type 'int ***'}}
|
return p; // expected-error {{cannot initialize return object of type 'const int ***' with an lvalue of type 'int ***'}}
|
||||||
// cxx2b-error@-1 {{cannot initialize return object of type 'const int ***' with an rvalue of type 'int ***'}}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b %s
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify %s
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20 %s
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
|
||||||
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20 %s
|
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s
|
||||||
|
|
||||||
int a;
|
int a;
|
||||||
int &b = [] (int &r) -> decltype(auto) { return r; } (a);
|
int &b = [] (int &r) -> decltype(auto) { return r; } (a);
|
||||||
|
@ -9,15 +9,13 @@ int &d = [] (int &r) -> auto & { return r; } (a);
|
||||||
int &e = [] (int &r) -> auto { return r; } (a); // expected-error {{cannot bind to a temporary}}
|
int &e = [] (int &r) -> auto { return r; } (a); // expected-error {{cannot bind to a temporary}}
|
||||||
int &f = [] (int r) -> decltype(auto) { return r; } (a); // expected-error {{cannot bind to a temporary}}
|
int &f = [] (int r) -> decltype(auto) { return r; } (a); // expected-error {{cannot bind to a temporary}}
|
||||||
int &g = [] (int r) -> decltype(auto) { return (r); } (a); // expected-warning {{reference to stack}}
|
int &g = [] (int r) -> decltype(auto) { return (r); } (a); // expected-warning {{reference to stack}}
|
||||||
// cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
|
|
||||||
|
|
||||||
int test_explicit_auto_return()
|
int test_explicit_auto_return()
|
||||||
{
|
{
|
||||||
struct X {};
|
struct X {};
|
||||||
auto L = [](auto F, auto a) { return F(a); };
|
auto L = [](auto F, auto a) { return F(a); };
|
||||||
auto M = [](auto a) -> auto { return a; }; // OK
|
auto M = [](auto a) -> auto { return a; }; // OK
|
||||||
auto MRef = [](auto b) -> auto & { return b; }; //cxx14_20-warning{{reference to stack}}
|
auto MRef = [](auto b) -> auto& { return b; }; //expected-warning{{reference to stack}}
|
||||||
// cxx2b-error@-1 {{non-const lvalue reference to type 'X' cannot bind to a temporary of type 'X'}}
|
|
||||||
auto MPtr = [](auto c) -> auto* { return &c; }; //expected-warning{{address of stack}}
|
auto MPtr = [](auto c) -> auto* { return &c; }; //expected-warning{{address of stack}}
|
||||||
auto MDeclType = [](auto&& d) -> decltype(auto) { return static_cast<decltype(d)>(d); }; //OK
|
auto MDeclType = [](auto&& d) -> decltype(auto) { return static_cast<decltype(d)>(d); }; //OK
|
||||||
M(3);
|
M(3);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b %s
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify %s
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx98_20 %s
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
|
||||||
// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify=expected,cxx98_20 %s
|
// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s
|
||||||
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98_20 %s
|
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||||
|
|
||||||
struct A {
|
struct A {
|
||||||
template <class T> operator T*();
|
template <class T> operator T*();
|
||||||
|
@ -67,10 +67,8 @@ struct X0 {
|
||||||
|
|
||||||
template<typename T> operator const T*() const {
|
template<typename T> operator const T*() const {
|
||||||
T x = T();
|
T x = T();
|
||||||
return x; // cxx98_20-error{{cannot initialize return object of type 'const char *' with an lvalue of type 'char'}} \
|
return x; // expected-error{{cannot initialize return object of type 'const char *' with an lvalue of type 'char'}} \
|
||||||
// cxx98_20-error{{cannot initialize return object of type 'const int *' with an lvalue of type 'int'}} \
|
// expected-error{{cannot initialize return object of type 'const int *' with an lvalue of type 'int'}}
|
||||||
// cxx2b-error{{cannot initialize return object of type 'const char *' with an rvalue of type 'char'}} \
|
|
||||||
// cxx2b-error{{cannot initialize return object of type 'const int *' with an rvalue of type 'int'}}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ L(2, t, X&);
|
||||||
|
|
||||||
// CHECK-LABEL: define{{.*}} void @_Z2l3v
|
// CHECK-LABEL: define{{.*}} void @_Z2l3v
|
||||||
// CHECK: call {{.*}} @_ZN1XC1Ev
|
// CHECK: call {{.*}} @_ZN1XC1Ev
|
||||||
|
// CHECK-NEXT: call {{.*}} @_ZN1XC1EOS_
|
||||||
|
// CHECK-NEXT: call void @llvm.lifetime.end
|
||||||
// CHECK-NEXT: call void @llvm.lifetime.end
|
// CHECK-NEXT: call void @llvm.lifetime.end
|
||||||
// CHECK-NEXT: ret void
|
// CHECK-NEXT: ret void
|
||||||
L(3, t, T);
|
L(3, t, T);
|
||||||
|
@ -150,11 +152,7 @@ F(8, (t), decltype(auto));
|
||||||
}; }()(); \
|
}; }()(); \
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK-LABEL: define{{.*}} void @_Z2b1v
|
//B(1, X); // Uncomment this line at your own peril ;)
|
||||||
// CHECK: call {{.*}} @_ZN1XC1Ev
|
|
||||||
// CHECK-NEXT: call void @llvm.lifetime.end
|
|
||||||
// CHECK-NEXT: ret void
|
|
||||||
B(1, X);
|
|
||||||
|
|
||||||
// CHECK-LABEL: define{{.*}} void @_Z2b2v
|
// CHECK-LABEL: define{{.*}} void @_Z2b2v
|
||||||
// CHECK: call {{.*}} @_ZN1XC1Ev
|
// CHECK: call {{.*}} @_ZN1XC1Ev
|
||||||
|
@ -166,6 +164,8 @@ B(2, X&);
|
||||||
|
|
||||||
// CHECK-LABEL: define{{.*}} void @_Z2b3v
|
// CHECK-LABEL: define{{.*}} void @_Z2b3v
|
||||||
// CHECK: call {{.*}} @_ZN1XC1Ev
|
// CHECK: call {{.*}} @_ZN1XC1Ev
|
||||||
|
// CHECK-NEXT: call {{.*}} @_ZN1XC1EOS_
|
||||||
|
// CHECK-NEXT: call void @llvm.lifetime.end
|
||||||
// CHECK-NEXT: call void @llvm.lifetime.end
|
// CHECK-NEXT: call void @llvm.lifetime.end
|
||||||
// CHECK-NEXT: ret void
|
// CHECK-NEXT: ret void
|
||||||
B(3, T);
|
B(3, T);
|
||||||
|
@ -187,24 +187,3 @@ B(4, T&);
|
||||||
B(5, );
|
B(5, );
|
||||||
|
|
||||||
#undef B
|
#undef B
|
||||||
|
|
||||||
// CHECK-LABEL: define{{.*}} void @_Z6f_attrv
|
|
||||||
// CHECK: call {{.*}} @_ZN1XC1Ev
|
|
||||||
// CHECK-NEXT: call void @llvm.lifetime.end
|
|
||||||
// CHECK-NEXT: ret void
|
|
||||||
template<class T = X> [[gnu::cdecl]] static inline auto tf_attr() -> X {
|
|
||||||
T t;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
void f_attr() { auto t = tf_attr(); }
|
|
||||||
|
|
||||||
// CHECK-LABEL: define{{.*}} void @_Z6b_attrv
|
|
||||||
// CHECK: call {{.*}} @_ZN1XC1Ev
|
|
||||||
// CHECK-NEXT: call void @llvm.lifetime.end
|
|
||||||
// CHECK-NEXT: ret void
|
|
||||||
void b_attr() {
|
|
||||||
auto t = []<class T = X>() { return ^ X () [[clang::vectorcall]] {
|
|
||||||
T t;
|
|
||||||
return t;
|
|
||||||
}; }()();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx11_20,cxx20_2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
|
||||||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11_20,cxx11 -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
|
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11 -triple x86_64-linux -Wno-string-plus-int -Wno-pointer-arith -Wno-zero-length-array -Wno-c99-designator -fcxx-exceptions -pedantic %s -Wno-comment -Wno-tautological-pointer-compare -Wno-bool-conversion
|
||||||
|
|
||||||
namespace StaticAssertFoldTest {
|
namespace StaticAssertFoldTest {
|
||||||
|
|
||||||
|
@ -1938,18 +1938,13 @@ namespace Lifetime {
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int &get(int &&n) { return n; }
|
constexpr int &get(int &&n) { return n; }
|
||||||
// cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
|
|
||||||
// cxx2b-error@-2 {{no return statement in constexpr function}} See PR40598
|
|
||||||
constexpr int &&get_rv(int &&n) { return static_cast<int&&>(n); }
|
constexpr int &&get_rv(int &&n) { return static_cast<int&&>(n); }
|
||||||
struct S {
|
struct S {
|
||||||
int &&r;
|
int &&r;
|
||||||
int &s;
|
int &s;
|
||||||
int t;
|
int t;
|
||||||
constexpr S() : r(get_rv(0)), s(get(0)), t(r) {} // expected-note {{read of object outside its lifetime}}
|
constexpr S() : r(get_rv(0)), s(get(0)), t(r) {} // expected-note {{read of object outside its lifetime}}
|
||||||
constexpr S(int) : r(get_rv(0)), s(get(0)), t(s) {}
|
constexpr S(int) : r(get_rv(0)), s(get(0)), t(s) {} // expected-note {{read of object outside its lifetime}}
|
||||||
// cxx2b-warning@-1 {{reference 's' is not yet bound to a value when used here}}
|
|
||||||
// cxx2b-note@-2 {{read of uninitialized object is not allowed in a constant expression}}
|
|
||||||
// cxx11_20-note@-3 {{read of object outside its lifetime}}
|
|
||||||
};
|
};
|
||||||
constexpr int k1 = S().t; // expected-error {{constant expression}} expected-note {{in call}}
|
constexpr int k1 = S().t; // expected-error {{constant expression}} expected-note {{in call}}
|
||||||
constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}}
|
constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -fcxx-exceptions -triple=x86_64-linux-gnu
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -triple=x86_64-linux-gnu
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b,cxx20 %s -fcxx-exceptions -triple=x86_64-linux-gnu
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -triple=x86_64-linux-gnu
|
||||||
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14 %s -fcxx-exceptions -triple=x86_64-linux-gnu
|
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14 %s -fcxx-exceptions -triple=x86_64-linux-gnu
|
||||||
|
|
||||||
struct S {
|
struct S {
|
||||||
// dummy ctor to make this a literal type
|
// dummy ctor to make this a literal type
|
||||||
|
@ -269,23 +269,16 @@ namespace null {
|
||||||
|
|
||||||
namespace incdec {
|
namespace incdec {
|
||||||
template<typename T> constexpr T &ref(T &&r) { return r; }
|
template<typename T> constexpr T &ref(T &&r) { return r; }
|
||||||
// cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
|
|
||||||
template<typename T> constexpr T postinc(T &&r) { return (r++, r); }
|
template<typename T> constexpr T postinc(T &&r) { return (r++, r); }
|
||||||
template<typename T> constexpr T postdec(T &&r) { return (r--, r); }
|
template<typename T> constexpr T postdec(T &&r) { return (r--, r); }
|
||||||
|
|
||||||
template int &ref<int>(int &&);
|
|
||||||
// cxx2b-note@-1 {{in instantiation of function template specialization}}
|
|
||||||
|
|
||||||
static_assert(postinc(0) == 1, "");
|
|
||||||
static_assert(postdec(0) == -1, "");
|
|
||||||
#if __cplusplus <= 202002L
|
|
||||||
static_assert(++ref(0) == 1, "");
|
static_assert(++ref(0) == 1, "");
|
||||||
static_assert(ref(0)++ == 0, "");
|
static_assert(ref(0)++ == 0, "");
|
||||||
|
static_assert(postinc(0) == 1, "");
|
||||||
static_assert(--ref(0) == -1, "");
|
static_assert(--ref(0) == -1, "");
|
||||||
static_assert(ref(0)-- == 0, "");
|
static_assert(ref(0)-- == 0, "");
|
||||||
#endif
|
static_assert(postdec(0) == -1, "");
|
||||||
|
|
||||||
#if __cplusplus <= 202002L
|
|
||||||
constexpr int overflow_int_inc_1 = ref(0x7fffffff)++; // expected-error {{constant}} expected-note {{2147483648}}
|
constexpr int overflow_int_inc_1 = ref(0x7fffffff)++; // expected-error {{constant}} expected-note {{2147483648}}
|
||||||
constexpr int overflow_int_inc_1_ok = ref(0x7ffffffe)++;
|
constexpr int overflow_int_inc_1_ok = ref(0x7ffffffe)++;
|
||||||
constexpr int overflow_int_inc_2 = ++ref(0x7fffffff); // expected-error {{constant}} expected-note {{2147483648}}
|
constexpr int overflow_int_inc_2 = ++ref(0x7fffffff); // expected-error {{constant}} expected-note {{2147483648}}
|
||||||
|
@ -298,42 +291,37 @@ namespace incdec {
|
||||||
// inc on bool sets to true
|
// inc on bool sets to true
|
||||||
static_assert(++ref(false), "");
|
static_assert(++ref(false), "");
|
||||||
// cxx14-warning@-1 {{incrementing expression of type bool}}
|
// cxx14-warning@-1 {{incrementing expression of type bool}}
|
||||||
// cxx20-error@-2 {{incrementing expression of type bool}}
|
// cxx20_2b-error@-2 {{incrementing expression of type bool}}
|
||||||
static_assert(++ref(true), "");
|
static_assert(++ref(true), "");
|
||||||
// cxx14-warning@-1 {{incrementing expression of type bool}}
|
// cxx14-warning@-1 {{incrementing expression of type bool}}
|
||||||
// cxx20-error@-2 {{incrementing expression of type bool}}
|
// cxx20_2b-error@-2 {{incrementing expression of type bool}}
|
||||||
#endif
|
|
||||||
|
|
||||||
int arr[10];
|
int arr[10];
|
||||||
static_assert(postinc(&arr[0]) == &arr[1], "");
|
|
||||||
static_assert(postdec(&arr[1]) == &arr[0], "");
|
|
||||||
#if __cplusplus <= 202002L
|
|
||||||
static_assert(++ref(&arr[0]) == &arr[1], "");
|
static_assert(++ref(&arr[0]) == &arr[1], "");
|
||||||
static_assert(++ref(&arr[9]) == &arr[10], "");
|
static_assert(++ref(&arr[9]) == &arr[10], "");
|
||||||
static_assert(++ref(&arr[10]) == &arr[11], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}}
|
static_assert(++ref(&arr[10]) == &arr[11], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}}
|
||||||
static_assert(ref(&arr[0])++ == &arr[0], "");
|
static_assert(ref(&arr[0])++ == &arr[0], "");
|
||||||
static_assert(ref(&arr[10])++ == &arr[10], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}}
|
static_assert(ref(&arr[10])++ == &arr[10], ""); // expected-error {{constant}} expected-note {{cannot refer to element 11}}
|
||||||
|
static_assert(postinc(&arr[0]) == &arr[1], "");
|
||||||
static_assert(--ref(&arr[10]) == &arr[9], "");
|
static_assert(--ref(&arr[10]) == &arr[9], "");
|
||||||
static_assert(--ref(&arr[1]) == &arr[0], "");
|
static_assert(--ref(&arr[1]) == &arr[0], "");
|
||||||
static_assert(--ref(&arr[0]) != &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}}
|
static_assert(--ref(&arr[0]) != &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}}
|
||||||
static_assert(ref(&arr[1])-- == &arr[1], "");
|
static_assert(ref(&arr[1])-- == &arr[1], "");
|
||||||
static_assert(ref(&arr[0])-- == &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}}
|
static_assert(ref(&arr[0])-- == &arr[0], ""); // expected-error {{constant}} expected-note {{cannot refer to element -1}}
|
||||||
#endif
|
static_assert(postdec(&arr[1]) == &arr[0], "");
|
||||||
|
|
||||||
static_assert(postinc(0.0) == 1.0, "");
|
|
||||||
static_assert(postdec(0.0) == -1.0, "");
|
|
||||||
#if __cplusplus <= 202002L
|
|
||||||
int x;
|
int x;
|
||||||
static_assert(++ref(&x) == &x + 1, "");
|
static_assert(++ref(&x) == &x + 1, "");
|
||||||
|
|
||||||
static_assert(++ref(0.0) == 1.0, "");
|
static_assert(++ref(0.0) == 1.0, "");
|
||||||
static_assert(ref(0.0)++ == 0.0, "");
|
static_assert(ref(0.0)++ == 0.0, "");
|
||||||
|
static_assert(postinc(0.0) == 1.0, "");
|
||||||
static_assert(--ref(0.0) == -1.0, "");
|
static_assert(--ref(0.0) == -1.0, "");
|
||||||
static_assert(ref(0.0)-- == 0.0, "");
|
static_assert(ref(0.0)-- == 0.0, "");
|
||||||
|
static_assert(postdec(0.0) == -1.0, "");
|
||||||
|
|
||||||
static_assert(++ref(1e100) == 1e100, "");
|
static_assert(++ref(1e100) == 1e100, "");
|
||||||
static_assert(--ref(1e100) == 1e100, "");
|
static_assert(--ref(1e100) == 1e100, "");
|
||||||
#endif
|
|
||||||
|
|
||||||
union U {
|
union U {
|
||||||
int a, b;
|
int a, b;
|
||||||
|
@ -875,13 +863,9 @@ namespace VirtualFromBase {
|
||||||
|
|
||||||
namespace Lifetime {
|
namespace Lifetime {
|
||||||
constexpr int &get(int &&r) { return r; }
|
constexpr int &get(int &&r) { return r; }
|
||||||
// cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
|
|
||||||
// cxx2b-error@-2 {{no return statement in constexpr function}} See PR40598
|
|
||||||
constexpr int f() {
|
constexpr int f() {
|
||||||
int &r = get(123);
|
int &r = get(123);
|
||||||
return r;
|
return r; // expected-note {{read of object outside its lifetime}}
|
||||||
// cxx2b-note@-1 {{use of reference outside its lifetime is not allowed in a constant expression}}
|
|
||||||
// cxx14_20-note@-2 {{read of object outside its lifetime}}
|
|
||||||
}
|
}
|
||||||
static_assert(f() == 123, ""); // expected-error {{constant expression}} expected-note {{in call}}
|
static_assert(f() == 123, ""); // expected-error {{constant expression}} expected-note {{in call}}
|
||||||
|
|
||||||
|
|
|
@ -39,14 +39,15 @@ struct suspend_never {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MoveOnly {
|
struct MoveOnly {
|
||||||
MoveOnly() = default;
|
MoveOnly() {};
|
||||||
MoveOnly(const MoveOnly&) = delete;
|
MoveOnly(const MoveOnly&) = delete;
|
||||||
MoveOnly(MoveOnly &&) = default;
|
MoveOnly(MoveOnly&&) noexcept {};
|
||||||
|
~MoveOnly() {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NoCopyNoMove {
|
struct NoCopyNoMove {
|
||||||
NoCopyNoMove() = default;
|
NoCopyNoMove() = default;
|
||||||
NoCopyNoMove(const NoCopyNoMove &) = delete;
|
NoCopyNoMove(const NoCopyNoMove &) = delete; // expected-note 4{{'NoCopyNoMove' has been explicitly marked deleted here}}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -62,12 +63,13 @@ struct task {
|
||||||
|
|
||||||
task<NoCopyNoMove> local2val() {
|
task<NoCopyNoMove> local2val() {
|
||||||
NoCopyNoMove value;
|
NoCopyNoMove value;
|
||||||
co_return value;
|
co_return value; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}}
|
||||||
|
// expected-error@-1 {{value reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}}
|
||||||
}
|
}
|
||||||
|
|
||||||
task<NoCopyNoMove &> local2ref() {
|
task<NoCopyNoMove &> local2ref() {
|
||||||
NoCopyNoMove value;
|
NoCopyNoMove value;
|
||||||
co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
|
co_return value; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need the move constructor for construction of the coroutine.
|
// We need the move constructor for construction of the coroutine.
|
||||||
|
@ -80,7 +82,8 @@ task<NoCopyNoMove> lvalue2val(NoCopyNoMove &value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
task<NoCopyNoMove> rvalue2val(NoCopyNoMove &&value) {
|
task<NoCopyNoMove> rvalue2val(NoCopyNoMove &&value) {
|
||||||
co_return value;
|
co_return value; // expected-error {{rvalue reference to type 'NoCopyNoMove' cannot bind to lvalue of type 'NoCopyNoMove'}}
|
||||||
|
// expected-error@-1 {{call to deleted constructor of 'NoCopyNoMove'}}
|
||||||
}
|
}
|
||||||
|
|
||||||
task<NoCopyNoMove &> lvalue2ref(NoCopyNoMove &value) {
|
task<NoCopyNoMove &> lvalue2ref(NoCopyNoMove &value) {
|
||||||
|
@ -88,7 +91,7 @@ task<NoCopyNoMove &> lvalue2ref(NoCopyNoMove &value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
task<NoCopyNoMove &> rvalue2ref(NoCopyNoMove &&value) {
|
task<NoCopyNoMove &> rvalue2ref(NoCopyNoMove &&value) {
|
||||||
co_return value; // expected-error {{non-const lvalue reference to type 'NoCopyNoMove' cannot bind to a temporary of type 'NoCopyNoMove'}}
|
co_return value; // expected-error {{call to deleted constructor of 'NoCopyNoMove'}}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct To {
|
struct To {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// This file contains references to sections of the Coroutines TS, which can be
|
// This file contains references to sections of the Coroutines TS, which can be
|
||||||
// found at http://wg21.link/coroutines.
|
// found at http://wg21.link/coroutines.
|
||||||
|
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -fcxx-exceptions -fexceptions -Wunused-result
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s -fcxx-exceptions -fexceptions -Wunused-result
|
||||||
// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -fsyntax-only -verify=expected,cxx14_20 %s -fcxx-exceptions -fexceptions -Wunused-result
|
// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -fsyntax-only -verify=expected %s -fcxx-exceptions -fexceptions -Wunused-result
|
||||||
|
|
||||||
void no_coroutine_traits_bad_arg_await() {
|
void no_coroutine_traits_bad_arg_await() {
|
||||||
co_await a; // expected-error {{include <experimental/coroutine>}}
|
co_await a; // expected-error {{include <experimental/coroutine>}}
|
||||||
|
@ -934,8 +934,7 @@ struct std::experimental::coroutine_traits<int, mismatch_gro_type_tag2> {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" int f(mismatch_gro_type_tag2) {
|
extern "C" int f(mismatch_gro_type_tag2) {
|
||||||
// cxx2b-error@-1 {{cannot initialize return object of type 'int' with an rvalue of type 'void *'}}
|
// expected-error@-1 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}}
|
||||||
// cxx14_20-error@-2 {{cannot initialize return object of type 'int' with an lvalue of type 'void *'}}
|
|
||||||
co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
|
co_return; //expected-note {{function is a coroutine due to use of 'co_return' here}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b,cxx2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx20_2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
|
||||||
|
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx20_2b %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
|
||||||
|
|
||||||
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14 %s
|
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14 %s
|
||||||
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14 %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
|
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14 %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
|
||||||
|
|
||||||
auto f(); // expected-note {{previous}}
|
auto f(); // expected-note {{previous}}
|
||||||
int f(); // expected-error {{differ only in their return type}}
|
int f(); // expected-error {{differ only in their return type}}
|
||||||
|
@ -129,14 +129,10 @@ namespace Templates {
|
||||||
return T() + 1;
|
return T() + 1;
|
||||||
}
|
}
|
||||||
template<typename T> auto &f2(T &&v) { return v; }
|
template<typename T> auto &f2(T &&v) { return v; }
|
||||||
// cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
|
|
||||||
// cxx2b-error@-2 {{non-const lvalue reference to type 'double' cannot bind to a temporary of type 'double'}}
|
|
||||||
// cxx2b-note@-3 {{candidate template ignored: substitution failure [with T = double]}}
|
|
||||||
int a = f1<int>();
|
int a = f1<int>();
|
||||||
const int &b = f2(0); // cxx2b-note {{in instantiation of function template specialization 'Templates::f2<int>' requested here}}
|
const int &b = f2(0);
|
||||||
double d;
|
double d;
|
||||||
float &c = f2(0.0); // expected-error {{non-const lvalue reference to type 'float' cannot bind to a value of unrelated type 'double'}}
|
float &c = f2(0.0); // expected-error {{non-const lvalue reference to type 'float' cannot bind to a value of unrelated type 'double'}}
|
||||||
// cxx2b-note@-1 {{in instantiation of function template specialization 'Templates::f2<double>' requested here}}
|
|
||||||
|
|
||||||
template<typename T> auto fwd_decl(); // expected-note {{declared here}}
|
template<typename T> auto fwd_decl(); // expected-note {{declared here}}
|
||||||
int e = fwd_decl<int>(); // expected-error {{cannot be used before it is defined}}
|
int e = fwd_decl<int>(); // expected-error {{cannot be used before it is defined}}
|
||||||
|
@ -149,9 +145,8 @@ namespace Templates {
|
||||||
auto (*p)() = f1; // expected-error {{incompatible initializer}}
|
auto (*p)() = f1; // expected-error {{incompatible initializer}}
|
||||||
auto (*q)() = f1<int>; // ok
|
auto (*q)() = f1<int>; // ok
|
||||||
|
|
||||||
typedef decltype(f2(1.2)) dbl; // cxx14_20-note {{previous}}
|
typedef decltype(f2(1.2)) dbl; // expected-note {{previous}}
|
||||||
// cxx2b-error@-1 {{no matching function for call to 'f2'}}
|
typedef float dbl; // expected-error {{typedef redefinition with different types ('float' vs 'decltype(f2(1.2))' (aka 'double &'))}}
|
||||||
typedef float dbl; // cxx14_20-error {{typedef redefinition with different types ('float' vs 'decltype(f2(1.2))' (aka 'double &'))}}
|
|
||||||
|
|
||||||
extern template auto fwd_decl<double>();
|
extern template auto fwd_decl<double>();
|
||||||
int k1 = fwd_decl<double>();
|
int k1 = fwd_decl<double>();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b %s
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected %s
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx11_20 %s
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected %s
|
||||||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11_20,cxx11 %s
|
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=expected,cxx11 %s
|
||||||
|
|
||||||
int* ret_local() {
|
int* ret_local() {
|
||||||
int x = 1;
|
int x = 1;
|
||||||
|
@ -29,8 +29,7 @@ int* ret_local_array_element_const_index() {
|
||||||
|
|
||||||
int& ret_local_ref() {
|
int& ret_local_ref() {
|
||||||
int x = 1;
|
int x = 1;
|
||||||
return x; // cxx11_20-warning {{reference to stack memory}}
|
return x; // expected-warning {{reference to stack memory}}
|
||||||
// cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int* ret_local_addrOf() {
|
int* ret_local_addrOf() {
|
||||||
|
@ -155,10 +154,8 @@ void ret_from_lambda() {
|
||||||
(void) [&]() -> int& { return b; };
|
(void) [&]() -> int& { return b; };
|
||||||
(void) [=]() mutable -> int& { return a; };
|
(void) [=]() mutable -> int& { return a; };
|
||||||
(void) [=]() mutable -> int& { return b; };
|
(void) [=]() mutable -> int& { return b; };
|
||||||
(void) [&]() -> int& { int a; return a; }; // cxx11_20-warning {{reference to stack}}
|
(void) [&]() -> int& { int a; return a; }; // expected-warning {{reference to stack}}
|
||||||
// cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
|
(void) [=]() -> int& { int a; return a; }; // expected-warning {{reference to stack}}
|
||||||
(void) [=]() -> int& { int a; return a; }; // cxx11_20-warning {{reference to stack}}
|
|
||||||
// cxx2b-error@-1 {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
|
|
||||||
(void) [&]() -> int& { int &a = b; return a; };
|
(void) [&]() -> int& { int &a = b; return a; };
|
||||||
(void) [=]() mutable -> int& { int &a = b; return a; };
|
(void) [=]() mutable -> int& { int &a = b; return a; };
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=cxx20_2b,cxx2b -fcxx-exceptions -Wreturn-std-move %s
|
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=cxx20_2b -fcxx-exceptions -Wreturn-std-move %s
|
||||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20_2b -fcxx-exceptions -Wreturn-std-move %s
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=cxx20_2b -fcxx-exceptions -Wreturn-std-move %s
|
||||||
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify=cxx11_17 -fcxx-exceptions -Wreturn-std-move %s
|
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify=cxx11_17 -fcxx-exceptions -Wreturn-std-move %s
|
||||||
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=cxx11_17 -fcxx-exceptions -Wreturn-std-move %s
|
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=cxx11_17 -fcxx-exceptions -Wreturn-std-move %s
|
||||||
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=cxx11_17 -fcxx-exceptions -Wreturn-std-move %s
|
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify=cxx11_17 -fcxx-exceptions -Wreturn-std-move %s
|
||||||
|
|
||||||
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
|
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
|
||||||
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
|
// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -Wreturn-std-move -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s -check-prefix=CHECK
|
||||||
|
@ -217,8 +217,8 @@ ConvertFromBase testRParam6(Derived&& d) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// But if the return type is a reference type, then moving would be wrong.
|
// But if the return type is a reference type, then moving would be wrong.
|
||||||
Derived &testRetRef1(Derived &&d) { return d; } // cxx2b-error {{non-const lvalue reference to type 'Derived' cannot bind to a temporary of type 'Derived'}}
|
Derived& testRetRef1(Derived&& d) { return d; }
|
||||||
Base &testRetRef2(Derived &&d) { return d; } // cxx2b-error {{non-const lvalue reference to type 'Base' cannot bind to a temporary of type 'Derived'}}
|
Base& testRetRef2(Derived&& d) { return d; }
|
||||||
#if __cplusplus >= 201402L
|
#if __cplusplus >= 201402L
|
||||||
auto&& testRetRef3(Derived&& d) { return d; }
|
auto&& testRetRef3(Derived&& d) { return d; }
|
||||||
decltype(auto) testRetRef4(Derived&& d) { return (d); }
|
decltype(auto) testRetRef4(Derived&& d) { return (d); }
|
||||||
|
|
Loading…
Reference in New Issue