Skip to content

Commit 47ccfd7

Browse files
committed
[Clang] Implement P2741R3 - user-generated static_assert messages
Reviewed By: #clang-language-wg, aaron.ballman Differential Revision: https://reviews.llvm.org/D154290
1 parent 1c154bd commit 47ccfd7

18 files changed

+631
-82
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ C++2c Feature Support
137137
- Implemented `P2738R1: constexpr cast from void* <https://wg21.link/P2738R1>`_.
138138
- Partially implemented `P2361R6: Unevaluated strings <https://wg21.link/P2361R6>`_.
139139
The changes to attributes declarations are not part of this release.
140+
- Implemented `P2741R3: user-generated static_assert messages <https://wg21.link/P2741R3>`_.
140141

141142
Resolutions to C++ Defect Reports
142143
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/DeclCXX.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4010,12 +4010,12 @@ class UnresolvedUsingIfExistsDecl final : public NamedDecl {
40104010
/// Represents a C++11 static_assert declaration.
40114011
class StaticAssertDecl : public Decl {
40124012
llvm::PointerIntPair<Expr *, 1, bool> AssertExprAndFailed;
4013-
StringLiteral *Message;
4013+
Expr *Message;
40144014
SourceLocation RParenLoc;
40154015

40164016
StaticAssertDecl(DeclContext *DC, SourceLocation StaticAssertLoc,
4017-
Expr *AssertExpr, StringLiteral *Message,
4018-
SourceLocation RParenLoc, bool Failed)
4017+
Expr *AssertExpr, Expr *Message, SourceLocation RParenLoc,
4018+
bool Failed)
40194019
: Decl(StaticAssert, DC, StaticAssertLoc),
40204020
AssertExprAndFailed(AssertExpr, Failed), Message(Message),
40214021
RParenLoc(RParenLoc) {}
@@ -4027,15 +4027,15 @@ class StaticAssertDecl : public Decl {
40274027

40284028
static StaticAssertDecl *Create(ASTContext &C, DeclContext *DC,
40294029
SourceLocation StaticAssertLoc,
4030-
Expr *AssertExpr, StringLiteral *Message,
4030+
Expr *AssertExpr, Expr *Message,
40314031
SourceLocation RParenLoc, bool Failed);
40324032
static StaticAssertDecl *CreateDeserialized(ASTContext &C, unsigned ID);
40334033

40344034
Expr *getAssertExpr() { return AssertExprAndFailed.getPointer(); }
40354035
const Expr *getAssertExpr() const { return AssertExprAndFailed.getPointer(); }
40364036

4037-
StringLiteral *getMessage() { return Message; }
4038-
const StringLiteral *getMessage() const { return Message; }
4037+
Expr *getMessage() { return Message; }
4038+
const Expr *getMessage() const { return Message; }
40394039

40404040
bool isFailed() const { return AssertExprAndFailed.getInt(); }
40414041

clang/include/clang/AST/Expr.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,11 @@ class Expr : public ValueStmt {
762762
/// strlen, false otherwise.
763763
bool tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const;
764764

765+
bool EvaluateCharRangeAsString(std::string &Result,
766+
const Expr *SizeExpression,
767+
const Expr *PtrExpression, ASTContext &Ctx,
768+
EvalResult &Status) const;
769+
765770
/// Enumeration used to describe the kind of Null pointer constant
766771
/// returned from \c isNullPointerConstant().
767772
enum NullPointerConstantKind {

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ def err_typecheck_converted_constant_expression_indirect : Error<
8383
"bind reference to a temporary">;
8484
def err_expr_not_cce : Error<
8585
"%select{case value|enumerator value|non-type template argument|"
86-
"array size|explicit specifier argument|noexcept specifier argument}0 "
87-
"is not a constant expression">;
86+
"array size|explicit specifier argument|noexcept specifier argument|"
87+
"call to 'size()'|call to 'data()'}0 is not a constant expression">;
8888
def ext_cce_narrowing : ExtWarn<
8989
"%select{case value|enumerator value|non-type template argument|"
9090
"array size|explicit specifier argument|noexcept specifier argument}0 "
@@ -1545,6 +1545,24 @@ def err_static_assert_requirement_failed : Error<
15451545
"static assertion failed due to requirement '%0'%select{: %2|}1">;
15461546
def note_expr_evaluates_to : Note<
15471547
"expression evaluates to '%0 %1 %2'">;
1548+
def err_static_assert_invalid_message : Error<
1549+
"the message in a static assertion must be a string literal or an "
1550+
"object with 'data()' and 'size()' member functions">;
1551+
def err_static_assert_missing_member_function : Error<
1552+
"the message object in this static assertion is missing %select{"
1553+
"a 'size()' member function|"
1554+
"a 'data()' member function|"
1555+
"'data()' and 'size()' member functions}0">;
1556+
def err_static_assert_invalid_mem_fn_ret_ty : Error<
1557+
"the message in a static assertion must have a '%select{size|data}0()' member "
1558+
"function returning an object convertible to '%select{std::size_t|const char *}0'">;
1559+
def warn_static_assert_message_constexpr : Warning<
1560+
"the message in this static assertion is not a "
1561+
"constant expression">,
1562+
DefaultError, InGroup<DiagGroup<"invalid-static-assert-message">>;
1563+
def err_static_assert_message_constexpr : Error<
1564+
"the message in a static assertion must be produced by a "
1565+
"constant expression">;
15481566

15491567
def warn_consteval_if_always_true : Warning<
15501568
"consteval if is always true in an %select{unevaluated|immediate}0 context">,

clang/include/clang/Sema/Sema.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3887,8 +3887,17 @@ class Sema final {
38873887
CCEK_TemplateArg, ///< Value of a non-type template parameter.
38883888
CCEK_ArrayBound, ///< Array bound in array declarator or new-expression.
38893889
CCEK_ExplicitBool, ///< Condition in an explicit(bool) specifier.
3890-
CCEK_Noexcept ///< Condition in a noexcept(bool) specifier.
3890+
CCEK_Noexcept, ///< Condition in a noexcept(bool) specifier.
3891+
CCEK_StaticAssertMessageSize, ///< Call to size() in a static assert
3892+
///< message.
3893+
CCEK_StaticAssertMessageData, ///< Call to data() in a static assert
3894+
///< message.
38913895
};
3896+
3897+
ExprResult BuildConvertedConstantExpression(Expr *From, QualType T,
3898+
CCEKind CCE,
3899+
NamedDecl *Dest = nullptr);
3900+
38923901
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
38933902
llvm::APSInt &Value, CCEKind CCE);
38943903
ExprResult CheckConvertedConstantExpression(Expr *From, QualType T,
@@ -7795,15 +7804,16 @@ class Sema final {
77957804
void UnmarkAsLateParsedTemplate(FunctionDecl *FD);
77967805
bool IsInsideALocalClassWithinATemplateFunction();
77977806

7807+
bool EvaluateStaticAssertMessageAsString(Expr *Message, std::string &Result,
7808+
ASTContext &Ctx,
7809+
bool ErrorOnInvalidMessage);
77987810
Decl *ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc,
77997811
Expr *AssertExpr,
78007812
Expr *AssertMessageExpr,
78017813
SourceLocation RParenLoc);
78027814
Decl *BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
7803-
Expr *AssertExpr,
7804-
StringLiteral *AssertMessageExpr,
7805-
SourceLocation RParenLoc,
7806-
bool Failed);
7815+
Expr *AssertExpr, Expr *AssertMessageExpr,
7816+
SourceLocation RParenLoc, bool Failed);
78077817
void DiagnoseStaticAssertDetails(const Expr *E);
78087818

78097819
FriendDecl *CheckFriendTypeDecl(SourceLocation LocStart,

clang/lib/AST/DeclCXX.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3237,8 +3237,7 @@ void StaticAssertDecl::anchor() {}
32373237

32383238
StaticAssertDecl *StaticAssertDecl::Create(ASTContext &C, DeclContext *DC,
32393239
SourceLocation StaticAssertLoc,
3240-
Expr *AssertExpr,
3241-
StringLiteral *Message,
3240+
Expr *AssertExpr, Expr *Message,
32423241
SourceLocation RParenLoc,
32433242
bool Failed) {
32443243
return new (C, DC) StaticAssertDecl(DC, StaticAssertLoc, AssertExpr, Message,

clang/lib/AST/DeclPrinter.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -949,9 +949,9 @@ void DeclPrinter::VisitStaticAssertDecl(StaticAssertDecl *D) {
949949
Out << "static_assert(";
950950
D->getAssertExpr()->printPretty(Out, nullptr, Policy, Indentation, "\n",
951951
&Context);
952-
if (StringLiteral *SL = D->getMessage()) {
952+
if (Expr *E = D->getMessage()) {
953953
Out << ", ";
954-
SL->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context);
954+
E->printPretty(Out, nullptr, Policy, Indentation, "\n", &Context);
955955
}
956956
Out << ")";
957957
}

clang/lib/AST/ExprConstant.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "clang/AST/StmtVisitor.h"
5151
#include "clang/AST/TypeLoc.h"
5252
#include "clang/Basic/Builtins.h"
53+
#include "clang/Basic/DiagnosticSema.h"
5354
#include "clang/Basic/TargetInfo.h"
5455
#include "llvm/ADT/APFixedPoint.h"
5556
#include "llvm/ADT/SmallBitVector.h"
@@ -1995,7 +1996,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
19951996

19961997
// ... a null pointer value, or a prvalue core constant expression of type
19971998
// std::nullptr_t.
1998-
if (!B) return true;
1999+
if (!B)
2000+
return true;
19992001

20002002
if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>()) {
20012003
// ... the address of an object with static storage duration,
@@ -2126,6 +2128,7 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) {
21262128
Info.Note((*Alloc)->AllocExpr->getExprLoc(),
21272129
diag::note_constexpr_dynamic_alloc_here);
21282130
}
2131+
21292132
// We have no information to show for a typeid(T) object.
21302133
}
21312134

@@ -16379,6 +16382,45 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result,
1637916382
}
1638016383
}
1638116384

16385+
bool Expr::EvaluateCharRangeAsString(std::string &Result,
16386+
const Expr *SizeExpression,
16387+
const Expr *PtrExpression, ASTContext &Ctx,
16388+
EvalResult &Status) const {
16389+
LValue String;
16390+
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
16391+
Info.InConstantContext = true;
16392+
16393+
FullExpressionRAII Scope(Info);
16394+
APSInt SizeValue;
16395+
if (!::EvaluateInteger(SizeExpression, SizeValue, Info))
16396+
return false;
16397+
16398+
int64_t Size = SizeValue.getExtValue();
16399+
16400+
if (!::EvaluatePointer(PtrExpression, String, Info))
16401+
return false;
16402+
16403+
QualType CharTy = PtrExpression->getType()->getPointeeType();
16404+
for (int64_t I = 0; I < Size; ++I) {
16405+
APValue Char;
16406+
if (!handleLValueToRValueConversion(Info, PtrExpression, CharTy, String,
16407+
Char))
16408+
return false;
16409+
16410+
APSInt C = Char.getInt();
16411+
Result.push_back(static_cast<char>(C.getExtValue()));
16412+
if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1))
16413+
return false;
16414+
}
16415+
if (!Scope.destroy())
16416+
return false;
16417+
16418+
if (!CheckMemoryLeaks(Info))
16419+
return false;
16420+
16421+
return true;
16422+
}
16423+
1638216424
bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const {
1638316425
Expr::EvalStatus Status;
1638416426
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);

clang/lib/AST/ODRDiagsEmitter.cpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -994,40 +994,43 @@ bool ODRDiagsEmitter::diagnoseMismatch(
994994
return true;
995995
}
996996

997-
const StringLiteral *FirstStr = FirstSA->getMessage();
998-
const StringLiteral *SecondStr = SecondSA->getMessage();
999-
assert((FirstStr || SecondStr) && "Both messages cannot be empty");
1000-
if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) {
997+
const Expr *FirstMessage = FirstSA->getMessage();
998+
const Expr *SecondMessage = SecondSA->getMessage();
999+
assert((FirstMessage || SecondMessage) && "Both messages cannot be empty");
1000+
if ((FirstMessage && !SecondMessage) || (!FirstMessage && SecondMessage)) {
10011001
SourceLocation FirstLoc, SecondLoc;
10021002
SourceRange FirstRange, SecondRange;
1003-
if (FirstStr) {
1004-
FirstLoc = FirstStr->getBeginLoc();
1005-
FirstRange = FirstStr->getSourceRange();
1003+
if (FirstMessage) {
1004+
FirstLoc = FirstMessage->getBeginLoc();
1005+
FirstRange = FirstMessage->getSourceRange();
10061006
} else {
10071007
FirstLoc = FirstSA->getBeginLoc();
10081008
FirstRange = FirstSA->getSourceRange();
10091009
}
1010-
if (SecondStr) {
1011-
SecondLoc = SecondStr->getBeginLoc();
1012-
SecondRange = SecondStr->getSourceRange();
1010+
if (SecondMessage) {
1011+
SecondLoc = SecondMessage->getBeginLoc();
1012+
SecondRange = SecondMessage->getSourceRange();
10131013
} else {
10141014
SecondLoc = SecondSA->getBeginLoc();
10151015
SecondRange = SecondSA->getSourceRange();
10161016
}
10171017
DiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)
1018-
<< (FirstStr == nullptr);
1018+
<< (FirstMessage == nullptr);
10191019
DiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)
1020-
<< (SecondStr == nullptr);
1020+
<< (SecondMessage == nullptr);
10211021
return true;
10221022
}
10231023

1024-
if (FirstStr && SecondStr &&
1025-
FirstStr->getString() != SecondStr->getString()) {
1026-
DiagError(FirstStr->getBeginLoc(), FirstStr->getSourceRange(),
1027-
StaticAssertMessage);
1028-
DiagNote(SecondStr->getBeginLoc(), SecondStr->getSourceRange(),
1029-
StaticAssertMessage);
1030-
return true;
1024+
if (FirstMessage && SecondMessage) {
1025+
unsigned FirstMessageODRHash = computeODRHash(FirstMessage);
1026+
unsigned SecondMessageODRHash = computeODRHash(SecondMessage);
1027+
if (FirstMessageODRHash != SecondMessageODRHash) {
1028+
DiagError(FirstMessage->getBeginLoc(), FirstMessage->getSourceRange(),
1029+
StaticAssertMessage);
1030+
DiagNote(SecondMessage->getBeginLoc(), SecondMessage->getSourceRange(),
1031+
StaticAssertMessage);
1032+
return true;
1033+
}
10311034
}
10321035
break;
10331036
}

clang/lib/Frontend/InitPreprocessor.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -629,8 +629,10 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
629629
Builder.defineMacro("__cpp_constexpr_in_decltype", "201711L");
630630
Builder.defineMacro("__cpp_range_based_for",
631631
LangOpts.CPlusPlus17 ? "201603L" : "200907");
632-
Builder.defineMacro("__cpp_static_assert",
633-
LangOpts.CPlusPlus17 ? "201411L" : "200410");
632+
Builder.defineMacro("__cpp_static_assert", LangOpts.CPlusPlus26 ? "202306L"
633+
: LangOpts.CPlusPlus17
634+
? "201411L"
635+
: "200410");
634636
Builder.defineMacro("__cpp_decltype", "200707L");
635637
Builder.defineMacro("__cpp_attributes", "200809L");
636638
Builder.defineMacro("__cpp_rvalue_references", "200610L");

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,14 +1016,17 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd) {
10161016
return nullptr;
10171017
}
10181018

1019-
if (!isTokenStringLiteral()) {
1019+
if (isTokenStringLiteral())
1020+
AssertMessage = ParseUnevaluatedStringLiteralExpression();
1021+
else if (getLangOpts().CPlusPlus26)
1022+
AssertMessage = ParseConstantExpressionInExprEvalContext();
1023+
else {
10201024
Diag(Tok, diag::err_expected_string_literal)
10211025
<< /*Source='static_assert'*/ 1;
10221026
SkipMalformedDecl();
10231027
return nullptr;
10241028
}
10251029

1026-
AssertMessage = ParseUnevaluatedStringLiteralExpression();
10271030
if (AssertMessage.isInvalid()) {
10281031
SkipMalformedDecl();
10291032
return nullptr;

0 commit comments

Comments
 (0)