summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/SemaExpr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema/SemaExpr.cpp')
-rw-r--r--clang/lib/Sema/SemaExpr.cpp352
1 files changed, 281 insertions, 71 deletions
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 2b714a36e2a..5cc3fb616df 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -625,15 +625,18 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
Context.getTargetInfo().getCXXABI().isMicrosoft())
(void)isCompleteType(E->getExprLoc(), T);
- UpdateMarkingForLValueToRValue(E);
+ ExprResult Res = CheckLValueToRValueConversionOperand(E);
+ if (Res.isInvalid())
+ return Res;
+ E = Res.get();
// Loading a __weak object implicitly retains the value, so we need a cleanup to
// balance that.
if (E->getType().getObjCLifetime() == Qualifiers::OCL_Weak)
Cleanup.setExprNeedsCleanups(true);
- ExprResult Res = ImplicitCastExpr::Create(Context, T, CK_LValueToRValue, E,
- nullptr, VK_RValue);
+ Res = ImplicitCastExpr::Create(Context, T, CK_LValueToRValue, E, nullptr,
+ VK_RValue);
// C11 6.3.2.1p2:
// ... if the lvalue has atomic type, the value has the non-atomic version
@@ -1794,9 +1797,19 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK,
isa<VarDecl>(D) &&
NeedToCaptureVariable(cast<VarDecl>(D), NameInfo.getLoc());
+ NonOdrUseReason NOUR;
+ if (isUnevaluatedContext())
+ NOUR = NOUR_Unevaluated;
+ else if (isa<VarDecl>(D) && D->getType()->isReferenceType() &&
+ !(getLangOpts().OpenMP && isOpenMPCapturedDecl(D)) &&
+ cast<VarDecl>(D)->isUsableInConstantExpressions(Context))
+ NOUR = NOUR_Constant;
+ else
+ NOUR = NOUR_None;
+
DeclRefExpr *E = DeclRefExpr::Create(Context, NNS, TemplateKWLoc, D,
RefersToCapturedVariable, NameInfo, Ty,
- VK, FoundD, TemplateArgs);
+ VK, FoundD, TemplateArgs, NOUR);
MarkDeclRefReferenced(E);
if (getLangOpts().ObjCWeak && isa<VarDecl>(D) &&
@@ -5626,7 +5639,8 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
NDecl = FDecl;
Fn = DeclRefExpr::Create(
Context, FDecl->getQualifierLoc(), SourceLocation(), FDecl, false,
- SourceLocation(), FDecl->getType(), Fn->getValueKind(), FDecl);
+ SourceLocation(), FDecl->getType(), Fn->getValueKind(), FDecl,
+ nullptr, DRE->isNonOdrUse());
}
}
} else if (isa<MemberExpr>(NakedFn))
@@ -15779,59 +15793,258 @@ QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) {
return DeclRefType;
}
+/// Walk the set of potential results of an expression and mark them all as
+/// non-odr-uses if they satisfy the side-conditions of the NonOdrUseReason.
+///
+/// \return A new expression if we found any potential results, ExprEmpty() if
+/// not, and ExprError() if we diagnosed an error.
+static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E,
+ NonOdrUseReason NOUR) {
+ // Per C++11 [basic.def.odr], a variable is odr-used "unless it is
+ // an object that satisfies the requirements for appearing in a
+ // constant expression (5.19) and the lvalue-to-rvalue conversion (4.1)
+ // is immediately applied." This function handles the lvalue-to-rvalue
+ // conversion part.
+ //
+ // If we encounter a node that claims to be an odr-use but shouldn't be, we
+ // transform it into the relevant kind of non-odr-use node and rebuild the
+ // tree of nodes leading to it.
+ //
+ // This is a mini-TreeTransform that only transforms a restricted subset of
+ // nodes (and only certain operands of them).
+ // Rebuild a subexpression.
+ auto Rebuild = [&](Expr *Sub) {
+ return rebuildPotentialResultsAsNonOdrUsed(S, Sub, NOUR);
+ };
-// If either the type of the variable or the initializer is dependent,
-// return false. Otherwise, determine whether the variable is a constant
-// expression. Use this if you need to know if a variable that might or
-// might not be dependent is truly a constant expression.
-static inline bool IsVariableNonDependentAndAConstantExpression(VarDecl *Var,
- ASTContext &Context) {
+ // Check whether a potential result satisfies the requirements of NOUR.
+ auto IsPotentialResultOdrUsed = [&](NamedDecl *D) {
+ // Any entity other than a VarDecl is always odr-used whenever it's named
+ // in a potentially-evaluated expression.
+ auto *VD = dyn_cast<VarDecl>(D);
+ if (!VD)
+ return true;
- if (Var->getType()->isDependentType())
- return false;
- const VarDecl *DefVD = nullptr;
- Var->getAnyInitializer(DefVD);
- if (!DefVD)
- return false;
- EvaluatedStmt *Eval = DefVD->ensureEvaluatedStmt();
- Expr *Init = cast<Expr>(Eval->Value);
- if (Init->isValueDependent())
+ // C++2a [basic.def.odr]p4:
+ // A variable x whose name appears as a potentially-evalauted expression
+ // e is odr-used by e unless
+ // -- x is a reference that is usable in constant expressions, or
+ // -- x is a variable of non-reference type that is usable in constant
+ // expressions and has no mutable subobjects, and e is an element of
+ // the set of potential results of an expression of
+ // non-volatile-qualified non-class type to which the lvalue-to-rvalue
+ // conversion is applied, or
+ // -- x is a variable of non-reference type, and e is an element of the
+ // set of potential results of a discarded-value expression to which
+ // the lvalue-to-rvalue conversion is not applied
+ //
+ // We check the first bullet and the "potentially-evaluated" condition in
+ // BuildDeclRefExpr. We check the type requirements in the second bullet
+ // in CheckLValueToRValueConversionOperand below.
+ switch (NOUR) {
+ case NOUR_None:
+ case NOUR_Unevaluated:
+ llvm_unreachable("unexpected non-odr-use-reason");
+
+ case NOUR_Constant:
+ // Constant references were handled when they were built.
+ if (VD->getType()->isReferenceType())
+ return true;
+ if (auto *RD = VD->getType()->getAsCXXRecordDecl())
+ if (RD->hasMutableFields())
+ return true;
+ if (!VD->isUsableInConstantExpressions(S.Context))
+ return true;
+ break;
+
+ case NOUR_Discarded:
+ if (VD->getType()->isReferenceType())
+ return true;
+ break;
+ }
return false;
- return IsVariableAConstantExpression(Var, Context);
-}
+ };
+ // Mark that this expression does not constitute an odr-use.
+ auto MarkNotOdrUsed = [&] {
+ S.MaybeODRUseExprs.erase(E);
+ if (LambdaScopeInfo *LSI = S.getCurLambda())
+ LSI->markVariableExprAsNonODRUsed(E);
+ };
-void Sema::UpdateMarkingForLValueToRValue(Expr *E) {
- // Per C++11 [basic.def.odr], a variable is odr-used "unless it is
- // an object that satisfies the requirements for appearing in a
- // constant expression (5.19) and the lvalue-to-rvalue conversion (4.1)
- // is immediately applied." This function handles the lvalue-to-rvalue
- // conversion part.
- MaybeODRUseExprs.erase(E->IgnoreParens());
+ // C++2a [basic.def.odr]p2:
+ // The set of potential results of an expression e is defined as follows:
+ switch (E->getStmtClass()) {
+ // -- If e is an id-expression, ...
+ case Expr::DeclRefExprClass: {
+ auto *DRE = cast<DeclRefExpr>(E);
+ if (DRE->isNonOdrUse() || IsPotentialResultOdrUsed(DRE->getDecl()))
+ break;
- // If we are in a lambda, check if this DeclRefExpr or MemberExpr refers
- // to a variable that is a constant expression, and if so, identify it as
- // a reference to a variable that does not involve an odr-use of that
- // variable.
- if (LambdaScopeInfo *LSI = getCurLambda()) {
- Expr *SansParensExpr = E->IgnoreParens();
- VarDecl *Var;
- ArrayRef<VarDecl *> Vars(&Var, &Var + 1);
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(SansParensExpr))
- Var = dyn_cast<VarDecl>(DRE->getFoundDecl());
- else if (MemberExpr *ME = dyn_cast<MemberExpr>(SansParensExpr))
- Var = dyn_cast<VarDecl>(ME->getMemberDecl());
- else if (auto *FPPE = dyn_cast<FunctionParmPackExpr>(SansParensExpr))
- Vars = llvm::makeArrayRef(FPPE->begin(), FPPE->end());
- else
- Vars = None;
+ // Rebuild as a non-odr-use DeclRefExpr.
+ MarkNotOdrUsed();
+ TemplateArgumentListInfo TemplateArgStorage, *TemplateArgs = nullptr;
+ if (DRE->hasExplicitTemplateArgs()) {
+ DRE->copyTemplateArgumentsInto(TemplateArgStorage);
+ TemplateArgs = &TemplateArgStorage;
+ }
+ return DeclRefExpr::Create(
+ S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(),
+ DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(),
+ DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(),
+ DRE->getFoundDecl(), TemplateArgs, NOUR);
+ }
+
+ case Expr::FunctionParmPackExprClass: {
+ auto *FPPE = cast<FunctionParmPackExpr>(E);
+ // If any of the declarations in the pack is odr-used, then the expression
+ // as a whole constitutes an odr-use.
+ for (VarDecl *D : *FPPE)
+ if (IsPotentialResultOdrUsed(D))
+ return ExprEmpty();
+
+ // FIXME: Rebuild as a non-odr-use FunctionParmPackExpr? In practice,
+ // nothing cares about whether we marked this as an odr-use, but it might
+ // be useful for non-compiler tools.
+ MarkNotOdrUsed();
+ break;
+ }
+
+ // FIXME: Implement these.
+ // -- If e is a subscripting operation with an array operand...
+ // -- If e is a class member access expression [...] naming a non-static
+ // data member...
+
+ // -- If e is a class member access expression naming a static data member,
+ // ...
+ case Expr::MemberExprClass: {
+ auto *ME = cast<MemberExpr>(E);
+ if (ME->getMemberDecl()->isCXXInstanceMember())
+ // FIXME: Recurse to the left-hand side.
+ break;
+
+ // FIXME: Track whether a MemberExpr constitutes an odr-use; bail out here
+ // if we've already marked it.
+ if (IsPotentialResultOdrUsed(ME->getMemberDecl()))
+ break;
+
+ // FIXME: Rebuild as a non-odr-use MemberExpr.
+ MarkNotOdrUsed();
+ return ExprEmpty();
+ }
+
+ // FIXME: Implement this.
+ // -- If e is a pointer-to-member expression of the form e1 .* e2 ...
+
+ // -- If e has the form (e1)...
+ case Expr::ParenExprClass: {
+ auto *PE = dyn_cast<ParenExpr>(E);
+ ExprResult Sub = Rebuild(PE->getSubExpr());
+ if (!Sub.isUsable())
+ return Sub;
+ return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get());
+ }
+
+ // FIXME: Implement these.
+ // -- If e is a glvalue conditional expression, ...
+ // -- If e is a comma expression, ...
- for (VarDecl *VD : Vars) {
- if (VD && IsVariableNonDependentAndAConstantExpression(VD, Context))
- LSI->markVariableExprAsNonODRUsed(SansParensExpr);
+ // [Clang extension]
+ // -- If e has the form __extension__ e1...
+ case Expr::UnaryOperatorClass: {
+ auto *UO = cast<UnaryOperator>(E);
+ if (UO->getOpcode() != UO_Extension)
+ break;
+ ExprResult Sub = Rebuild(UO->getSubExpr());
+ if (!Sub.isUsable())
+ return Sub;
+ return S.BuildUnaryOp(nullptr, UO->getOperatorLoc(), UO_Extension,
+ Sub.get());
+ }
+
+ // [Clang extension]
+ // -- If e has the form _Generic(...), the set of potential results is the
+ // union of the sets of potential results of the associated expressions.
+ case Expr::GenericSelectionExprClass: {
+ auto *GSE = dyn_cast<GenericSelectionExpr>(E);
+
+ SmallVector<Expr *, 4> AssocExprs;
+ bool AnyChanged = false;
+ for (Expr *OrigAssocExpr : GSE->getAssocExprs()) {
+ ExprResult AssocExpr = Rebuild(OrigAssocExpr);
+ if (AssocExpr.isInvalid())
+ return ExprError();
+ if (AssocExpr.isUsable()) {
+ AssocExprs.push_back(AssocExpr.get());
+ AnyChanged = true;
+ } else {
+ AssocExprs.push_back(OrigAssocExpr);
+ }
}
+
+ return AnyChanged ? S.CreateGenericSelectionExpr(
+ GSE->getGenericLoc(), GSE->getDefaultLoc(),
+ GSE->getRParenLoc(), GSE->getControllingExpr(),
+ GSE->getAssocTypeSourceInfos(), AssocExprs)
+ : ExprEmpty();
+ }
+
+ // [Clang extension]
+ // -- If e has the form __builtin_choose_expr(...), the set of potential
+ // results is the union of the sets of potential results of the
+ // second and third subexpressions.
+ case Expr::ChooseExprClass: {
+ auto *CE = dyn_cast<ChooseExpr>(E);
+
+ ExprResult LHS = Rebuild(CE->getLHS());
+ if (LHS.isInvalid())
+ return ExprError();
+
+ ExprResult RHS = Rebuild(CE->getLHS());
+ if (RHS.isInvalid())
+ return ExprError();
+
+ if (!LHS.get() && !RHS.get())
+ return ExprEmpty();
+ if (!LHS.isUsable())
+ LHS = CE->getLHS();
+ if (!RHS.isUsable())
+ RHS = CE->getRHS();
+
+ return S.ActOnChooseExpr(CE->getBuiltinLoc(), CE->getCond(), LHS.get(),
+ RHS.get(), CE->getRParenLoc());
}
+
+ // Step through non-syntactic nodes.
+ case Expr::ConstantExprClass: {
+ auto *CE = dyn_cast<ConstantExpr>(E);
+ ExprResult Sub = Rebuild(CE->getSubExpr());
+ if (!Sub.isUsable())
+ return Sub;
+ return ConstantExpr::Create(S.Context, Sub.get());
+ }
+
+ default:
+ break;
+ }
+
+ // Can't traverse through this node. Nothing to do.
+ return ExprEmpty();
+}
+
+ExprResult Sema::CheckLValueToRValueConversionOperand(Expr *E) {
+ // C++2a [basic.def.odr]p4:
+ // [...] an expression of non-volatile-qualified non-class type to which
+ // the lvalue-to-rvalue conversion is applied [...]
+ if (E->getType().isVolatileQualified() || E->getType()->getAs<RecordType>())
+ return E;
+
+ ExprResult Result =
+ rebuildPotentialResultsAsNonOdrUsed(*this, E, NOUR_Constant);
+ if (Result.isInvalid())
+ return ExprError();
+ return Result.get() ? Result : E;
}
ExprResult Sema::ActOnConstantExpression(ExprResult Res) {
@@ -15844,8 +16057,7 @@ ExprResult Sema::ActOnConstantExpression(ExprResult Res) {
// deciding whether it is an odr-use, just assume we will apply the
// lvalue-to-rvalue conversion. In the one case where this doesn't happen
// (a non-type template argument), we have special handling anyway.
- UpdateMarkingForLValueToRValue(Res.get());
- return Res;
+ return CheckLValueToRValueConversionOperand(Res.get());
}
void Sema::CleanupVarDeclMarking() {
@@ -15889,7 +16101,7 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
OdrUseContext OdrUse = isOdrUseContext(SemaRef);
bool UsableInConstantExpr =
- Var->isUsableInConstantExpressions(SemaRef.Context);
+ Var->mightBeUsableInConstantExpressions(SemaRef.Context);
// C++20 [expr.const]p12:
// A variable [...] is needed for constant evaluation if it is [...] a
@@ -15964,7 +16176,7 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
}
}
- // C++20 [basic.def.odr]p4:
+ // C++2a [basic.def.odr]p4:
// A variable x whose name appears as a potentially-evaluated expression e
// is odr-used by e unless
// -- x is a reference that is usable in constant expressions
@@ -15978,11 +16190,14 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
// lvalue-to-rvalue conversion is not applied [FIXME]
//
// We check the first part of the second bullet here, and
- // Sema::UpdateMarkingForLValueToRValue deals with the second part.
+ // Sema::CheckLValueToRValueConversionOperand deals with the second part.
// FIXME: To get the third bullet right, we need to delay this even for
// variables that are not usable in constant expressions.
+ DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E);
switch (OdrUse) {
case OdrUseContext::None:
+ assert((!DRE || DRE->isNonOdrUse() == NOUR_Unevaluated) &&
+ "missing non-odr-use marking for unevaluated operand");
break;
case OdrUseContext::FormallyOdrUsed:
@@ -15991,19 +16206,21 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
break;
case OdrUseContext::Used:
- if (E && IsVariableAConstantExpression(Var, SemaRef.Context)) {
- // A reference initialized by a constant expression can never be
- // odr-used, so simply ignore it.
- if (!Var->getType()->isReferenceType() ||
- (SemaRef.LangOpts.OpenMP && SemaRef.isOpenMPCapturedDecl(Var)))
- SemaRef.MaybeODRUseExprs.insert(E);
- } else {
- MarkVarDeclODRUsed(Var, Loc, SemaRef,
- /*MaxFunctionScopeIndex ptr*/ nullptr);
- }
+ // If we already know this isn't an odr-use, there's nothing more to do.
+ if (DRE && DRE->isNonOdrUse())
+ break;
+ // If we might later find that this expression isn't actually an odr-use,
+ // delay the marking.
+ if (E && Var->isUsableInConstantExpressions(SemaRef.Context))
+ SemaRef.MaybeODRUseExprs.insert(E);
+ else
+ MarkVarDeclODRUsed(Var, Loc, SemaRef);
break;
case OdrUseContext::Dependent:
+ // If we already know this isn't an odr-use, there's nothing more to do.
+ if (DRE && DRE->isNonOdrUse())
+ break;
// If this is a dependent context, we don't need to mark variables as
// odr-used, but we may still need to track them for lambda capture.
// FIXME: Do we also need to do this inside dependent typeid expressions
@@ -16028,7 +16245,7 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
// FIXME: We can simplify this a lot after implementing P0588R1.
assert(E && "Capture variable should be used in an expression.");
if (!Var->getType()->isReferenceType() ||
- !IsVariableNonDependentAndAConstantExpression(Var, SemaRef.Context))
+ !Var->isUsableInConstantExpressions(SemaRef.Context))
LSI->addPotentialCapture(E->IgnoreParens());
}
}
@@ -16241,13 +16458,6 @@ namespace {
void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
Visit(E->getExpr());
}
-
- void VisitImplicitCastExpr(ImplicitCastExpr *E) {
- Inherited::VisitImplicitCastExpr(E);
-
- if (E->getCastKind() == CK_LValueToRValue)
- S.UpdateMarkingForLValueToRValue(E->getSubExpr());
- }
};
}
OpenPOWER on IntegriCloud