diff options
author | kadir çetinkaya <kadircet@google.com> | 2025-07-08 16:00:40 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-08 16:00:40 +0200 |
commit | f72e53f35070140cbd6d4acdf7f8bc37f72d0445 (patch) | |
tree | 4f9641dbe16fbb6c79010593bf6503bbc0e46a7a /clang/lib/AST/ExprConstant.cpp | |
parent | 39bc0529b018a89b4b6a21aaabe240cd3a65c44d (diff) | |
download | llvm-f72e53f35070140cbd6d4acdf7f8bc37f72d0445.zip llvm-f72e53f35070140cbd6d4acdf7f8bc37f72d0445.tar.gz llvm-f72e53f35070140cbd6d4acdf7f8bc37f72d0445.tar.bz2 |
[clang][CompundLiteralExpr] Don't defer evaluation for CLEs (#137163)
Previously we would defer evaluation of CLEs until LValue to RValue
conversions, which would result in creating values within wrong scope
and triggering use-after-frees.
This patch instead eagerly evaluates CLEs, within the scope requiring
them. This requires storing an extra pointer for CLE expressions with
static storage.
Fixes https://github.com/llvm/llvm-project/issues/137165
Diffstat (limited to 'clang/lib/AST/ExprConstant.cpp')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 90 |
1 files changed, 49 insertions, 41 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 81c778e..60c658a 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -48,6 +48,7 @@ #include "clang/AST/OptionalDiagnostic.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" +#include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DiagnosticSema.h" @@ -4534,6 +4535,30 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, BaseVal = MTE->getOrCreateValue(false); assert(BaseVal && "got reference to unevaluated temporary"); + } else if (const CompoundLiteralExpr *CLE = + dyn_cast_or_null<CompoundLiteralExpr>(Base)) { + // According to GCC info page: + // + // 6.28 Compound Literals + // + // As an optimization, G++ sometimes gives array compound literals + // longer lifetimes: when the array either appears outside a function or + // has a const-qualified type. If foo and its initializer had elements + // of type char *const rather than char *, or if foo were a global + // variable, the array would have static storage duration. But it is + // probably safest just to avoid the use of array compound literals in + // C++ code. + // + // Obey that rule by checking constness for converted array types. + if (QualType CLETy = CLE->getType(); CLETy->isArrayType() && + !LValType->isArrayType() && + !CLETy.isConstant(Info.Ctx)) { + Info.FFDiag(E); + Info.Note(CLE->getExprLoc(), diag::note_declared_at); + return CompleteObject(); + } + + BaseVal = &CLE->getStaticValue(); } else { if (!IsAccess) return CompleteObject(LVal.getLValueBase(), nullptr, BaseType); @@ -4599,44 +4624,7 @@ handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type, WantObjectRepresentation ? AK_ReadObjectRepresentation : AK_Read; if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) { - if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(Base)) { - // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the - // initializer until now for such expressions. Such an expression can't be - // an ICE in C, so this only matters for fold. - if (Type.isVolatileQualified()) { - Info.FFDiag(Conv); - return false; - } - - APValue Lit; - if (!Evaluate(Lit, Info, CLE->getInitializer())) - return false; - - // According to GCC info page: - // - // 6.28 Compound Literals - // - // As an optimization, G++ sometimes gives array compound literals longer - // lifetimes: when the array either appears outside a function or has a - // const-qualified type. If foo and its initializer had elements of type - // char *const rather than char *, or if foo were a global variable, the - // array would have static storage duration. But it is probably safest - // just to avoid the use of array compound literals in C++ code. - // - // Obey that rule by checking constness for converted array types. - - QualType CLETy = CLE->getType(); - if (CLETy->isArrayType() && !Type->isArrayType()) { - if (!CLETy.isConstant(Info.Ctx)) { - Info.FFDiag(Conv); - Info.Note(CLE->getExprLoc(), diag::note_declared_at); - return false; - } - } - - CompleteObject LitObj(LVal.Base, &Lit, Base->getType()); - return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal, AK); - } else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) { + if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) { // Special-case character extraction so we don't have to construct an // APValue for the whole string. assert(LVal.Designator.Entries.size() <= 1 && @@ -9144,9 +9132,29 @@ bool LValueExprEvaluator::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) { assert((!Info.getLangOpts().CPlusPlus || E->isFileScope()) && "lvalue compound literal in c++?"); - // Defer visiting the literal until the lvalue-to-rvalue conversion. We can - // only see this when folding in C, so there's no standard to follow here. - return Success(E); + APValue *Lit; + // If CompountLiteral has static storage, its value can be used outside + // this expression. So evaluate it once and store it in ASTContext. + if (E->hasStaticStorage()) { + Lit = &E->getOrCreateStaticValue(Info.Ctx); + Result.set(E); + // Reset any previously evaluated state, otherwise evaluation below might + // fail. + // FIXME: Should we just re-use the previously evaluated value instead? + *Lit = APValue(); + } else { + assert(!Info.getLangOpts().CPlusPlus); + Lit = &Info.CurrentCall->createTemporary(E, E->getInitializer()->getType(), + ScopeKind::Block, Result); + } + // FIXME: Evaluating in place isn't always right. We should figure out how to + // use appropriate evaluation context here, see + // clang/test/AST/static-compound-literals-reeval.cpp for a failure. + if (!EvaluateInPlace(*Lit, Info, Result, E->getInitializer())) { + *Lit = APValue(); + return false; + } + return true; } bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { |