diff options
Diffstat (limited to 'clang/lib/Sema/SemaExpr.cpp')
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 352 |
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()); - } }; } |