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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,7 @@ Bug Fixes to C++ Support
- Clang modules now allow a module and its user to differ on TrivialAutoVarInit*
- Fixed an access checking bug when initializing non-aggregates in default arguments (#GH62444), (#GH83608)
- Fixed a pack substitution bug in deducing class template partial specializations. (#GH53609)
- Fixed a crash when constant evaluating some explicit object member assignment operators. (#GH142835)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -1049,7 +1050,7 @@ Sanitizers
----------

- ``-fsanitize=vptr`` is no longer a part of ``-fsanitize=undefined``.
- Sanitizer ignorelists now support the syntax ``src:*=sanitize``,
- Sanitizer ignorelists now support the syntax ``src:*=sanitize``,
``type:*=sanitize``, ``fun:*=sanitize``, ``global:*=sanitize``,
and ``mainfile:*=sanitize``.

Expand Down
64 changes: 41 additions & 23 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6549,8 +6549,8 @@ static bool MaybeHandleUnionActiveMemberChange(EvalInfo &Info,
}

static bool EvaluateCallArg(const ParmVarDecl *PVD, const Expr *Arg,
CallRef Call, EvalInfo &Info,
bool NonNull = false) {
CallRef Call, EvalInfo &Info, bool NonNull = false,
APValue **EvaluatedArg = nullptr) {
LValue LV;
// Create the parameter slot and register its destruction. For a vararg
// argument, create a temporary.
Expand All @@ -6570,13 +6570,17 @@ static bool EvaluateCallArg(const ParmVarDecl *PVD, const Expr *Arg,
return false;
}

if (EvaluatedArg)
*EvaluatedArg = &V;

return true;
}

/// Evaluate the arguments to a function call.
static bool EvaluateArgs(ArrayRef<const Expr *> Args, CallRef Call,
EvalInfo &Info, const FunctionDecl *Callee,
bool RightToLeft = false) {
bool RightToLeft = false,
LValue *ObjectArg = nullptr) {
bool Success = true;
llvm::SmallBitVector ForbiddenNullArgs;
if (Callee->hasAttr<NonNullAttr>()) {
Expand All @@ -6599,13 +6603,16 @@ static bool EvaluateArgs(ArrayRef<const Expr *> Args, CallRef Call,
const ParmVarDecl *PVD =
Idx < Callee->getNumParams() ? Callee->getParamDecl(Idx) : nullptr;
bool NonNull = !ForbiddenNullArgs.empty() && ForbiddenNullArgs[Idx];
if (!EvaluateCallArg(PVD, Args[Idx], Call, Info, NonNull)) {
APValue *That = nullptr;
if (!EvaluateCallArg(PVD, Args[Idx], Call, Info, NonNull, &That)) {
// If we're checking for a potential constant expression, evaluate all
// initializers even if some of them fail.
if (!Info.noteFailure())
return false;
Success = false;
}
if (PVD && PVD->isExplicitObjectParameter() && That && That->isLValue())
ObjectArg->setFrom(Info.Ctx, *That);
}
return Success;
}
Expand Down Expand Up @@ -6633,14 +6640,15 @@ static bool handleTrivialCopy(EvalInfo &Info, const ParmVarDecl *Param,

/// Evaluate a function call.
static bool HandleFunctionCall(SourceLocation CallLoc,
const FunctionDecl *Callee, const LValue *This,
const Expr *E, ArrayRef<const Expr *> Args,
CallRef Call, const Stmt *Body, EvalInfo &Info,
const FunctionDecl *Callee,
const LValue *ObjectArg, const Expr *E,
ArrayRef<const Expr *> Args, CallRef Call,
const Stmt *Body, EvalInfo &Info,
APValue &Result, const LValue *ResultSlot) {
if (!Info.CheckCallLimit(CallLoc))
return false;

CallStackFrame Frame(Info, E->getSourceRange(), Callee, This, E, Call);
CallStackFrame Frame(Info, E->getSourceRange(), Callee, ObjectArg, E, Call);

// For a trivial copy or move assignment, perform an APValue copy. This is
// essential for unions, where the operations performed by the assignment
Expand All @@ -6653,16 +6661,20 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
(MD->getParent()->isUnion() ||
(MD->isTrivial() &&
isReadByLvalueToRvalueConversion(MD->getParent())))) {
assert(This &&
unsigned ExplicitOffset = MD->isExplicitObjectMemberFunction() ? 1 : 0;
assert(ObjectArg &&
(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()));
APValue RHSValue;
if (!handleTrivialCopy(Info, MD->getParamDecl(0), Args[0], RHSValue,
MD->getParent()->isUnion()))
return false;
if (!handleAssignment(Info, Args[0], *This, MD->getThisType(),

LValue Obj;
if (!handleAssignment(Info, Args[ExplicitOffset], *ObjectArg,
MD->getFunctionObjectParameterReferenceType(),
RHSValue))
return false;
This->moveInto(Result);
ObjectArg->moveInto(Result);
return true;
} else if (MD && isLambdaCallOperator(MD)) {
// We're in a lambda; determine the lambda capture field maps unless we're
Expand Down Expand Up @@ -8289,7 +8301,7 @@ class ExprEvaluatorBase
QualType CalleeType = Callee->getType();

const FunctionDecl *FD = nullptr;
LValue *This = nullptr, ThisVal;
LValue *This = nullptr, ObjectArg;
auto Args = llvm::ArrayRef(E->getArgs(), E->getNumArgs());
bool HasQualifier = false;

Expand All @@ -8300,28 +8312,28 @@ class ExprEvaluatorBase
const CXXMethodDecl *Member = nullptr;
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Callee)) {
// Explicit bound member calls, such as x.f() or p->g();
if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
if (!EvaluateObjectArgument(Info, ME->getBase(), ObjectArg))
return false;
Member = dyn_cast<CXXMethodDecl>(ME->getMemberDecl());
if (!Member)
return Error(Callee);
This = &ThisVal;
This = &ObjectArg;
HasQualifier = ME->hasQualifier();
} else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) {
// Indirect bound member calls ('.*' or '->*').
const ValueDecl *D =
HandleMemberPointerAccess(Info, BE, ThisVal, false);
HandleMemberPointerAccess(Info, BE, ObjectArg, false);
if (!D)
return false;
Member = dyn_cast<CXXMethodDecl>(D);
if (!Member)
return Error(Callee);
This = &ThisVal;
This = &ObjectArg;
} else if (const auto *PDE = dyn_cast<CXXPseudoDestructorExpr>(Callee)) {
if (!Info.getLangOpts().CPlusPlus20)
Info.CCEDiag(PDE, diag::note_constexpr_pseudo_destructor);
return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal) &&
HandleDestruction(Info, PDE, ThisVal, PDE->getDestroyedType());
return EvaluateObjectArgument(Info, PDE->getBase(), ObjectArg) &&
HandleDestruction(Info, PDE, ObjectArg, PDE->getDestroyedType());
} else
return Error(Callee);
FD = Member;
Expand Down Expand Up @@ -8358,7 +8370,7 @@ class ExprEvaluatorBase
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
HasThis = MD->isImplicitObjectMemberFunction();
if (!EvaluateArgs(HasThis ? Args.slice(1) : Args, Call, Info, FD,
/*RightToLeft=*/true))
/*RightToLeft=*/true, &ObjectArg))
return false;
}

Expand All @@ -8373,20 +8385,20 @@ class ExprEvaluatorBase
if (Args.empty())
return Error(E);

if (!EvaluateObjectArgument(Info, Args[0], ThisVal))
if (!EvaluateObjectArgument(Info, Args[0], ObjectArg))
return false;

// If we are calling a static operator, the 'this' argument needs to be
// ignored after being evaluated.
if (MD->isInstance())
This = &ThisVal;
This = &ObjectArg;

// If this is syntactically a simple assignment using a trivial
// assignment operator, start the lifetimes of union members as needed,
// per C++20 [class.union]5.
if (Info.getLangOpts().CPlusPlus20 && OCE &&
OCE->getOperator() == OO_Equal && MD->isTrivial() &&
!MaybeHandleUnionActiveMemberChange(Info, Args[0], ThisVal))
!MaybeHandleUnionActiveMemberChange(Info, Args[0], ObjectArg))
return false;

Args = Args.slice(1);
Expand Down Expand Up @@ -8441,7 +8453,8 @@ class ExprEvaluatorBase
// Evaluate the arguments now if we've not already done so.
if (!Call) {
Call = Info.CurrentCall->createCall(FD);
if (!EvaluateArgs(Args, Call, Info, FD))
if (!EvaluateArgs(Args, Call, Info, FD, /*RightToLeft*/ false,
&ObjectArg))
return false;
}

Expand Down Expand Up @@ -8475,6 +8488,11 @@ class ExprEvaluatorBase
Stmt *Body = FD->getBody(Definition);
SourceLocation Loc = E->getExprLoc();

// Treat the object argument as `this` when evaluating defaulted
// special menmber functions
if (FD->hasCXXExplicitFunctionObjectParameter())
This = &ObjectArg;

if (!CheckConstexprFunction(Info, Loc, FD, Definition, Body) ||
!HandleFunctionCall(Loc, Definition, This, E, Args, Call, Body, Info,
Result, ResultSlot))
Expand Down
29 changes: 29 additions & 0 deletions clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,32 @@ int test() {
}

}

namespace GH142835 {
struct MoveMe {
MoveMe& operator=(this MoveMe&, const MoveMe&) = default;
constexpr MoveMe& operator=(this MoveMe& self, MoveMe&& other) {
self.value = other.value;
other.value = 0;
return self;
}
int value = 4242;
};

struct S {
constexpr S& operator=(this S&, const S&) = default;
S& operator=(this S&, S&&) = default;

MoveMe move_me;
};

constexpr bool f() {
S s1{};
S s2{};
s2 = s1;
return true;
}

static_assert(f());

}
Loading