git: ca3747034ef4 - main - Merge commit c52392a6f464 from llvm-project (by ShengYi Hung):
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 30 Sep 2026 16:33:01 UTC
The branch main has been updated by dim:
URL: https://cgit.FreeBSD.org/src/commit/?id=ca3747034ef484b024a202d213a2e08b16066791
commit ca3747034ef484b024a202d213a2e08b16066791
Author: Dimitry Andric <dim@FreeBSD.org>
AuthorDate: 2026-09-28 18:12:33 +0000
Commit: Dimitry Andric <dim@FreeBSD.org>
CommitDate: 2026-09-30 16:32:47 +0000
Merge commit c52392a6f464 from llvm-project (by ShengYi Hung):
[Clang][ExprConst] Drop PRValue for nothrow new (#226753)
A user defined operator new can accept prvalue for nothrow. However, it
should not be a ConstExpr. Early returns by
isUsableAsGlobalAllocationFunctionInConstantEvaluation instead of doing
LValue evaluation.
Also, move CheckPlacement new logic into new OpCode. This decouples
checking from Interp.cpp to Compiler.cpp.
This fixes "Assertion failed: (E->isGLValue() ||
E->getType()->isFunctionType() || E->getType()->isVoidType() ||
isa<ObjCSelectorExpr>(E->IgnoreParens())), function EvaluateLValue" when
building the databases/mariadb123-server port.
MFC after: 1 week
---
.../clang/lib/AST/ByteCode/Compiler.cpp | 20 ++++---
.../llvm-project/clang/lib/AST/ByteCode/Interp.cpp | 65 +++++++++++-----------
.../llvm-project/clang/lib/AST/ByteCode/Interp.h | 1 +
.../llvm-project/clang/lib/AST/ByteCode/Opcodes.td | 4 ++
.../llvm-project/clang/lib/AST/ExprConstant.cpp | 42 ++++++++------
5 files changed, 71 insertions(+), 61 deletions(-)
diff --git a/contrib/llvm-project/clang/lib/AST/ByteCode/Compiler.cpp b/contrib/llvm-project/clang/lib/AST/ByteCode/Compiler.cpp
index afa3b7ea7de7..04c3d6458de0 100644
--- a/contrib/llvm-project/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/contrib/llvm-project/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3456,19 +3456,21 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
// alignof(X) and X has new-extended alignment).
if (PlacementArgs == 1) {
const Expr *Arg1 = E->getPlacementArg(0);
- if (Arg1->getType()->isNothrowT()) {
+ if (OperatorNew->isReservedGlobalPlacementOperator()) {
+ if (!this->emitCheckPlacementNew(E, E))
+ return false;
+ PlacementDest = Arg1;
+ } else if (
+ Arg1->getType()->isNothrowT() &&
+ OperatorNew
+ ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
if (!this->discard(Arg1))
return false;
IsNoThrow = true;
} else {
- // Invalid unless we have C++26 or are in a std:: function.
- if (!this->emitInvalidNewDeleteExpr(E, E))
- return false;
-
- // If we have a placement-new destination, we'll later use that instead
- // of allocating.
- if (OperatorNew->isReservedGlobalPlacementOperator())
- PlacementDest = Arg1;
+ // Any other placement list is invalid. This includes a user-declared
+ // allocation function taking std::nothrow_t, e.g. by value.
+ return this->emitInvalidNewDeleteExpr(E, E);
}
} else {
// Always invalid.
diff --git a/contrib/llvm-project/clang/lib/AST/ByteCode/Interp.cpp b/contrib/llvm-project/clang/lib/AST/ByteCode/Interp.cpp
index 457de2bed37d..987e32911f81 100644
--- a/contrib/llvm-project/clang/lib/AST/ByteCode/Interp.cpp
+++ b/contrib/llvm-project/clang/lib/AST/ByteCode/Interp.cpp
@@ -1802,9 +1802,6 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
if (!S.inConstantContext() && isConstexprUnknown(Ptr))
return false;
- if (!InvalidNewDeleteExpr(S, OpPC, E))
- return false;
-
const auto *NewExpr = cast<CXXNewExpr>(E);
QualType StorageType = Ptr.getFieldDesc()->getDataType(S.getASTContext());
const ASTContext &ASTCtx = S.getASTContext();
@@ -1843,45 +1840,45 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
assert(E);
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) {
const FunctionDecl *OperatorNew = NewExpr->getOperatorNew();
- if (NewExpr->getNumPlacementArgs() > 0) {
- // This is allowed pre-C++26, but only an std function.
- if (S.getLangOpts().CPlusPlus26 || S.Current->isStdFunction())
- return true;
- S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_placement)
- << /*C++26 feature*/ 1 << E->getSourceRange();
- } else if (
- !OperatorNew
- ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
- S.FFDiag(S.Current->getSource(OpPC),
- diag::note_constexpr_new_non_replaceable)
- << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
- return false;
- } else if (!S.getLangOpts().CPlusPlus26 &&
- NewExpr->getNumPlacementArgs() == 1 &&
- !OperatorNew->isReservedGlobalPlacementOperator()) {
- if (!S.getLangOpts().CPlusPlus26) {
- S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_placement)
- << /*Unsupported*/ 0 << E->getSourceRange();
- return false;
- }
- return true;
- }
- } else {
- const auto *DeleteExpr = cast<CXXDeleteExpr>(E);
- const FunctionDecl *OperatorDelete = DeleteExpr->getOperatorDelete();
- if (!OperatorDelete
- ->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
- S.FFDiag(S.Current->getSource(OpPC),
- diag::note_constexpr_new_non_replaceable)
- << isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;
+ // The only new-placement list we support is (std::nothrow), and only for
+ // the replaceable global allocation functions.
+ bool IsNothrowForm = NewExpr->getNumPlacementArgs() == 1 &&
+ NewExpr->getPlacementArg(0)->getType()->isNothrowT();
+ if (NewExpr->getNumPlacementArgs() > 0 && !IsNothrowForm) {
+ S.FFDiag(Loc, diag::note_constexpr_new_placement)
+ << /*Unsupported*/ 0 << E->getSourceRange();
return false;
}
+
+ assert(
+ !OperatorNew->isUsableAsGlobalAllocationFunctionInConstantEvaluation());
+ S.FFDiag(Loc, diag::note_constexpr_new_non_replaceable)
+ << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
+ return false;
}
+ const auto *DeleteExpr = cast<CXXDeleteExpr>(E);
+ const FunctionDecl *OperatorDelete = DeleteExpr->getOperatorDelete();
+ assert(!OperatorDelete
+ ->isUsableAsGlobalAllocationFunctionInConstantEvaluation());
+ S.FFDiag(Loc, diag::note_constexpr_new_non_replaceable)
+ << isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;
+ return false;
+}
+
+bool CheckPlacementNew(InterpState &S, CodePtr OpPC, const Expr *E) {
+ // Placement new is allowed in C++26. Before that, it is only allowed in a
+ // std:: function or if [[msvc::constexpr]] was used.
+ if (S.getLangOpts().CPlusPlus26 || S.Current->isStdFunction())
+ return true;
+
+ S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_placement)
+ << /*C++26 feature*/ 1 << E->getSourceRange();
return false;
}
diff --git a/contrib/llvm-project/clang/lib/AST/ByteCode/Interp.h b/contrib/llvm-project/clang/lib/AST/ByteCode/Interp.h
index 9d17f96c97c8..17850c8510b1 100644
--- a/contrib/llvm-project/clang/lib/AST/ByteCode/Interp.h
+++ b/contrib/llvm-project/clang/lib/AST/ByteCode/Interp.h
@@ -3398,6 +3398,7 @@ bool CheckNewTypeMismatchArray(InterpState &S, CodePtr OpPC, const Expr *E) {
return CheckNewTypeMismatch(S, OpPC, E, static_cast<uint64_t>(Size));
}
bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E);
+bool CheckPlacementNew(InterpState &S, CodePtr OpPC, const Expr *E);
template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool BitCastPrim(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
diff --git a/contrib/llvm-project/clang/lib/AST/ByteCode/Opcodes.td b/contrib/llvm-project/clang/lib/AST/ByteCode/Opcodes.td
index 57e01f7bd9da..7d41c2c63334 100644
--- a/contrib/llvm-project/clang/lib/AST/ByteCode/Opcodes.td
+++ b/contrib/llvm-project/clang/lib/AST/ByteCode/Opcodes.td
@@ -851,6 +851,10 @@ def InvalidNewDeleteExpr : Opcode {
let Args = [ArgExpr];
}
+def CheckPlacementNew : Opcode {
+ let Args = [ArgExpr];
+}
+
def CheckNewTypeMismatchArray : Opcode {
let Types = [IntegerTypeClass];
let Args = [ArgExpr];
diff --git a/contrib/llvm-project/clang/lib/AST/ExprConstant.cpp b/contrib/llvm-project/clang/lib/AST/ExprConstant.cpp
index bfecae972940..fa2c42ab509c 100644
--- a/contrib/llvm-project/clang/lib/AST/ExprConstant.cpp
+++ b/contrib/llvm-project/clang/lib/AST/ExprConstant.cpp
@@ -10322,23 +10322,21 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
bool IsNothrow = false;
bool IsPlacement = false;
- if (E->getNumPlacementArgs() == 1 &&
- E->getPlacementArg(0)->getType()->isNothrowT()) {
- // The only new-placement list we support is of the form (std::nothrow).
- //
- // FIXME: There is no restriction on this, but it's not clear that any
- // other form makes any sense. We get here for cases such as:
- //
- // new (std::align_val_t{N}) X(int)
- //
- // (which should presumably be valid only if N is a multiple of
- // alignof(int), and in any case can't be deallocated unless N is
- // alignof(X) and X has new-extended alignment).
- LValue Nothrow;
- if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info))
- return false;
- IsNothrow = true;
- } else if (OperatorNew->isReservedGlobalPlacementOperator()) {
+ // The only new-placement list we support (other than the reserved placement
+ // form) is of the form (std::nothrow).
+ //
+ // FIXME: There is no restriction on this, but it's not clear that any
+ // other form makes any sense. We get here for cases such as:
+ //
+ // new (std::align_val_t{N}) X(int)
+ //
+ // (which should presumably be valid only if N is a multiple of
+ // alignof(int), and in any case can't be deallocated unless N is
+ // alignof(X) and X has new-extended alignment).
+ bool HasNothrowArg = E->getNumPlacementArgs() == 1 &&
+ E->getPlacementArg(0)->getType()->isNothrowT();
+
+ if (OperatorNew->isReservedGlobalPlacementOperator()) {
if (Info.CurrentCall->isStdFunction() || Info.getLangOpts().CPlusPlus26 ||
(Info.CurrentCall->CanEvalMSConstexpr &&
OperatorNew->hasAttr<MSConstexprAttr>())) {
@@ -10353,15 +10351,23 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
<< /*C++26 feature*/ 1 << E->getSourceRange();
return false;
}
- } else if (E->getNumPlacementArgs()) {
+ } else if (E->getNumPlacementArgs() && !HasNothrowArg) {
Info.FFDiag(E, diag::note_constexpr_new_placement)
<< /*Unsupported*/ 0 << E->getSourceRange();
return false;
} else if (!OperatorNew
->isUsableAsGlobalAllocationFunctionInConstantEvaluation()) {
+ // [expr.const] only permits new-expressions that select a replaceable
+ // global allocation function. Check this before evaluating a
+ // (std::nothrow) placement argument.
Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
<< isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
return false;
+ } else if (HasNothrowArg) {
+ LValue Nothrow;
+ if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info))
+ return false;
+ IsNothrow = true;
}
const Expr *Init = E->getInitializer();