From 84508166a9784701dad659881bf75f9764e53f54 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Wed, 12 Mar 2025 12:23:14 +0100 Subject: [PATCH 01/16] [Clang][WIP] Constant Expressions inside of gcc'asm strings Implements GCC's constexpr string ASM extension https://gcc.gnu.org/onlinedocs/gcc/Asm-constexprs.html --- clang/include/clang/AST/Expr.h | 5 + clang/include/clang/AST/RecursiveASTVisitor.h | 8 +- clang/include/clang/AST/Stmt.h | 50 +++--- .../clang/Basic/DiagnosticParseKinds.td | 5 +- .../clang/Basic/DiagnosticSemaKinds.td | 36 +++-- clang/include/clang/Sema/Sema.h | 25 ++- clang/lib/AST/ASTImporter.cpp | 12 +- clang/lib/AST/ExprConstant.cpp | 83 ++++++---- clang/lib/AST/Stmt.cpp | 103 ++++++++---- clang/lib/AST/StmtPrinter.cpp | 21 ++- clang/lib/AST/StmtProfile.cpp | 8 +- clang/lib/CodeGen/CGStmt.cpp | 16 +- clang/lib/Parse/ParseStmtAsm.cpp | 2 +- clang/lib/Parse/Parser.cpp | 37 +++-- clang/lib/Sema/SemaDeclCXX.cpp | 99 ++++++++---- clang/lib/Sema/SemaStmtAsm.cpp | 147 +++++++++++------- clang/lib/Sema/TreeTransform.h | 50 ++++-- clang/lib/Serialization/ASTReaderStmt.cpp | 10 +- clang/lib/Serialization/ASTWriterStmt.cpp | 8 +- 19 files changed, 473 insertions(+), 252 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index cfe49acf20b77..7f9e50dfe78bd 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -787,6 +787,11 @@ class Expr : public ValueStmt { const Expr *PtrExpression, ASTContext &Ctx, EvalResult &Status) const; + bool EvaluateCharRangeAsString(APValue &Result, + const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + EvalResult &Status) const; + /// If the current Expr can be evaluated to a pointer to a null-terminated /// constant string, return the constant string (without the terminating /// null). diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index fac4c10987157..87a6c22b35ee8 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2410,15 +2410,15 @@ DEF_TRAVERSE_DECL(ImplicitConceptSpecializationDecl, { } DEF_TRAVERSE_STMT(GCCAsmStmt, { - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmString()); + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getAsmStringExpr()); for (unsigned I = 0, E = S->getNumInputs(); I < E; ++I) { - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInputConstraintLiteral(I)); + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getInputConstraintExpr(I)); } for (unsigned I = 0, E = S->getNumOutputs(); I < E; ++I) { - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOutputConstraintLiteral(I)); + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOutputConstraintExpr(I)); } for (unsigned I = 0, E = S->getNumClobbers(); I < E; ++I) { - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getClobberStringLiteral(I)); + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getClobberExpr(I)); } // children() iterates over inputExpr and outputExpr. }) diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 604ac51d478cf..7bf0a6911c515 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -3193,7 +3193,7 @@ class AsmStmt : public Stmt { /// getOutputConstraint - Return the constraint string for the specified /// output operand. All output constraints are known to be non-empty (either /// '=' or '+'). - StringRef getOutputConstraint(unsigned i) const; + std::string getOutputConstraint(unsigned i) const; /// isOutputPlusConstraint - Return true if the specified output constraint /// is a "+" constraint (which is both an input and an output) or false if it @@ -3214,14 +3214,14 @@ class AsmStmt : public Stmt { /// getInputConstraint - Return the specified input constraint. Unlike output /// constraints, these can be empty. - StringRef getInputConstraint(unsigned i) const; + std::string getInputConstraint(unsigned i) const; const Expr *getInputExpr(unsigned i) const; //===--- Other ---===// unsigned getNumClobbers() const { return NumClobbers; } - StringRef getClobber(unsigned i) const; + std::string getClobber(unsigned i) const; static bool classof(const Stmt *T) { return T->getStmtClass() == GCCAsmStmtClass || @@ -3302,20 +3302,20 @@ class GCCAsmStmt : public AsmStmt { friend class ASTStmtReader; SourceLocation RParenLoc; - StringLiteral *AsmStr; + Expr *AsmStr; // FIXME: If we wanted to, we could allocate all of these in one big array. - StringLiteral **Constraints = nullptr; - StringLiteral **Clobbers = nullptr; + Expr **Constraints = nullptr; + Expr **Clobbers = nullptr; IdentifierInfo **Names = nullptr; unsigned NumLabels = 0; public: GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple, bool isvolatile, unsigned numoutputs, unsigned numinputs, - IdentifierInfo **names, StringLiteral **constraints, Expr **exprs, - StringLiteral *asmstr, unsigned numclobbers, - StringLiteral **clobbers, unsigned numlabels, + IdentifierInfo **names, Expr **constraints, Expr **exprs, + Expr *asmstr, unsigned numclobbers, + Expr **clobbers, unsigned numlabels, SourceLocation rparenloc); /// Build an empty inline-assembly statement. @@ -3326,9 +3326,11 @@ class GCCAsmStmt : public AsmStmt { //===--- Asm String Analysis ---===// - const StringLiteral *getAsmString() const { return AsmStr; } - StringLiteral *getAsmString() { return AsmStr; } - void setAsmString(StringLiteral *E) { AsmStr = E; } + const Expr *getAsmStringExpr() const { return AsmStr; } + Expr *getAsmStringExpr() { return AsmStr; } + void setAsmString(Expr *E) { AsmStr = E; } + + std::string getAsmString() const; /// AsmStringPiece - this is part of a decomposed asm string specification /// (for use with the AnalyzeAsmString function below). An asm string is @@ -3397,12 +3399,12 @@ class GCCAsmStmt : public AsmStmt { return {}; } - StringRef getOutputConstraint(unsigned i) const; + std::string getOutputConstraint(unsigned i) const; - const StringLiteral *getOutputConstraintLiteral(unsigned i) const { + const Expr *getOutputConstraintExpr(unsigned i) const { return Constraints[i]; } - StringLiteral *getOutputConstraintLiteral(unsigned i) { + Expr *getOutputConstraintExpr(unsigned i) { return Constraints[i]; } @@ -3425,12 +3427,12 @@ class GCCAsmStmt : public AsmStmt { return {}; } - StringRef getInputConstraint(unsigned i) const; + std::string getInputConstraint(unsigned i) const; - const StringLiteral *getInputConstraintLiteral(unsigned i) const { + const Expr *getInputConstraintExpr(unsigned i) const { return Constraints[i + NumOutputs]; } - StringLiteral *getInputConstraintLiteral(unsigned i) { + Expr *getInputConstraintExpr(unsigned i) { return Constraints[i + NumOutputs]; } @@ -3441,6 +3443,8 @@ class GCCAsmStmt : public AsmStmt { return const_cast(this)->getInputExpr(i); } + static std::string ExtractStringFromGCCAsmStmtComponent(const Expr* E); + //===--- Labels ---===// bool isAsmGoto() const { @@ -3489,12 +3493,12 @@ class GCCAsmStmt : public AsmStmt { private: void setOutputsAndInputsAndClobbers(const ASTContext &C, IdentifierInfo **Names, - StringLiteral **Constraints, + Expr **Constraints, Stmt **Exprs, unsigned NumOutputs, unsigned NumInputs, unsigned NumLabels, - StringLiteral **Clobbers, + Expr **Clobbers, unsigned NumClobbers); public: @@ -3505,10 +3509,10 @@ class GCCAsmStmt : public AsmStmt { /// This returns -1 if the operand name is invalid. int getNamedOperand(StringRef SymbolicName) const; - StringRef getClobber(unsigned i) const; + std::string getClobber(unsigned i) const; - StringLiteral *getClobberStringLiteral(unsigned i) { return Clobbers[i]; } - const StringLiteral *getClobberStringLiteral(unsigned i) const { + Expr *getClobberExpr(unsigned i) { return Clobbers[i]; } + const Expr *getClobberExpr(unsigned i) const { return Clobbers[i]; } diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 092a55f9e9e0c..4dc956f7ae6f7 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -336,7 +336,10 @@ def warn_cxx20_compat_label_end_of_compound_statement : Warning< def err_address_of_label_outside_fn : Error< "use of address-of-label extension outside of a function body">; def err_asm_operand_wide_string_literal : Error< - "cannot use %select{unicode|wide|an empty}0 string literal in 'asm'">; + "cannot use %select{unicode|wide}0 string literal in 'asm'">; + +def err_asm_expected_string : Error< + "expected string literal %select{or parenthesized constant expression |}0in 'asm'">; def err_expected_selector_for_method : Error< "expected selector for Objective-C method">; def err_expected_property_name : Error<"expected property name">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4bdbb797c2b86..2e8db1a2249e4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1663,23 +1663,30 @@ def err_static_assert_requirement_failed : Error< "static assertion failed due to requirement '%0'%select{: %2|}1">; def note_expr_evaluates_to : Note< "expression evaluates to '%0 %1 %2'">; -def err_static_assert_invalid_message : Error< - "the message in a static assertion must be a string literal or an " + + +def subst_user_defined_msg : TextSubstitution< + "%select{the message|the expression}0 in " + "%select{a static assertion|this asm operand}0">; + +def err_user_defined_msg_invalid : Error< + "%sub{subst_user_defined_msg}0 must be a string literal or an " "object with 'data()' and 'size()' member functions">; -def err_static_assert_missing_member_function : Error< - "the message object in this static assertion is missing %select{" +def err_user_defined_msg_missing_member_function : Error< + "the %select{message|string}0 object in " + "%select{this static assertion|this asm operand}0 is missing %select{" "a 'size()' member function|" "a 'data()' member function|" - "'data()' and 'size()' member functions}0">; -def err_static_assert_invalid_mem_fn_ret_ty : Error< - "the message in a static assertion must have a '%select{size|data}0()' member " - "function returning an object convertible to '%select{std::size_t|const char *}0'">; -def warn_static_assert_message_constexpr : Warning< - "the message in this static assertion is not a " - "constant expression">, + "'data()' and 'size()' member functions}1">; +def err_user_defined_msg_invalid_mem_fn_ret_ty : Error< + "%sub{subst_user_defined_msg}0 must have a '%select{size|data}1()' member " + "function returning an object convertible to '%select{std::size_t|const char *}1'">; +def warn_user_defined_msg_constexpr : Warning< + "%select{the message|the expression}0 in " + "%select{this static assertion|this asm operand}0 is not a constant expression">, DefaultError, InGroup>; -def err_static_assert_message_constexpr : Error< - "the message in a static assertion must be produced by a " +def err_user_defined_msg_constexpr : Error< + "%sub{subst_user_defined_msg}0 must be produced by a " "constant expression">; def warn_consteval_if_always_true : Warning< @@ -9514,6 +9521,9 @@ def warn_redefine_extname_not_applied : Warning< // inline asm. let CategoryName = "Inline Assembly Issue" in { + def err_asm_operand_empty_string : Error< + "cannot use an empty string literal in 'asm'">; + def err_asm_pmf_through_constraint_not_permitted : Error<"cannot pass a pointer-to-member through register-constrained " "inline assembly parameter">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c034de0e633da..e1b1083675612 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5585,9 +5585,18 @@ class Sema final : public SemaBase { void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method); void ActOnFinishDelayedMemberInitializers(Decl *Record); - bool EvaluateStaticAssertMessageAsString(Expr *Message, std::string &Result, - ASTContext &Ctx, - bool ErrorOnInvalidMessage); + enum class StringEvaluationContext { + StaticAssert = 0, + Asm = 1 + }; + + bool EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx, + StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage); + bool EvaluateAsString(Expr *Message, std::string &Result, ASTContext &Ctx, + StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage); + Decl *ActOnStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *AssertExpr, Expr *AssertMessageExpr, SourceLocation RParenLoc); @@ -11040,6 +11049,7 @@ class Sema final : public SemaBase { ///@{ public: + ExprResult ActOnGCCAsmStmtString(Expr* Stm, bool ForAsmLabel); StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, unsigned NumOutputs, unsigned NumInputs, IdentifierInfo **Names, @@ -15328,6 +15338,15 @@ void Sema::PragmaStack::Act(SourceLocation PragmaLocation, PragmaMsStackAction Action, llvm::StringRef StackSlotLabel, AlignPackInfo Value); + +inline +const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, + Sema::StringEvaluationContext Ctx) { + DB << llvm::to_underlying(Ctx); + return DB; +} + + } // end namespace clang #endif diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 82180486f3c7c..1b507761ddf70 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -6821,25 +6821,25 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) { Names.push_back(ToII); } - SmallVector Clobbers; + SmallVector Clobbers; for (unsigned I = 0, E = S->getNumClobbers(); I != E; I++) { - if (auto ClobberOrErr = import(S->getClobberStringLiteral(I))) + if (auto ClobberOrErr = import(S->getClobberExpr(I))) Clobbers.push_back(*ClobberOrErr); else return ClobberOrErr.takeError(); } - SmallVector Constraints; + SmallVector Constraints; for (unsigned I = 0, E = S->getNumOutputs(); I != E; I++) { - if (auto OutputOrErr = import(S->getOutputConstraintLiteral(I))) + if (auto OutputOrErr = import(S->getOutputConstraintExpr(I))) Constraints.push_back(*OutputOrErr); else return OutputOrErr.takeError(); } for (unsigned I = 0, E = S->getNumInputs(); I != E; I++) { - if (auto InputOrErr = import(S->getInputConstraintLiteral(I))) + if (auto InputOrErr = import(S->getInputConstraintExpr(I))) Constraints.push_back(*InputOrErr); else return InputOrErr.takeError(); @@ -6861,7 +6861,7 @@ ExpectedStmt ASTNodeImporter::VisitGCCAsmStmt(GCCAsmStmt *S) { ExpectedSLoc AsmLocOrErr = import(S->getAsmLoc()); if (!AsmLocOrErr) return AsmLocOrErr.takeError(); - auto AsmStrOrErr = import(S->getAsmString()); + auto AsmStrOrErr = import(S->getAsmStringExpr()); if (!AsmStrOrErr) return AsmStrOrErr.takeError(); ExpectedSLoc RParenLocOrErr = import(S->getRParenLoc()); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index f8e8aaddbfdbd..86fe8554cba85 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -17928,43 +17928,70 @@ std::optional Expr::tryEvaluateString(ASTContext &Ctx) const { return {}; } -bool Expr::EvaluateCharRangeAsString(std::string &Result, - const Expr *SizeExpression, - const Expr *PtrExpression, ASTContext &Ctx, - EvalResult &Status) const { - LValue String; - EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression); - Info.InConstantContext = true; +template +static bool EvaluateCharRangeAsStringImpl(const Expr*, T& Result, + const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + Expr::EvalResult &Status) { + LValue String; + EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression); + Info.InConstantContext = true; - FullExpressionRAII Scope(Info); - APSInt SizeValue; - if (!::EvaluateInteger(SizeExpression, SizeValue, Info)) - return false; + FullExpressionRAII Scope(Info); + APSInt SizeValue; + if (!::EvaluateInteger(SizeExpression, SizeValue, Info)) + return false; - uint64_t Size = SizeValue.getZExtValue(); + uint64_t Size = SizeValue.getZExtValue(); - if (!::EvaluatePointer(PtrExpression, String, Info)) - return false; + if constexpr(std::is_same_v) + Result = APValue(APValue::UninitArray{}, Size, Size); + //else + // Result.reserve(Size); - QualType CharTy = PtrExpression->getType()->getPointeeType(); - for (uint64_t I = 0; I < Size; ++I) { - APValue Char; - if (!handleLValueToRValueConversion(Info, PtrExpression, CharTy, String, - Char)) + if (!::EvaluatePointer(PtrExpression, String, Info)) + return false; + + QualType CharTy = PtrExpression->getType()->getPointeeType(); + for (uint64_t I = 0; I < Size; ++I) { + APValue Char; + if (!handleLValueToRValueConversion(Info, PtrExpression, CharTy, String, + Char)) + return false; + + if constexpr(std::is_same_v) { + Result.getArrayInitializedElt(I) = std::move(Char); + } + else { + APSInt C = Char.getInt(); + Result.push_back(static_cast(C.getExtValue())); + } + + if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1)) + return false; + } + if (!Scope.destroy()) return false; - APSInt C = Char.getInt(); - Result.push_back(static_cast(C.getExtValue())); - if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1)) + if (!CheckMemoryLeaks(Info)) return false; - } - if (!Scope.destroy()) - return false; - if (!CheckMemoryLeaks(Info)) - return false; + return true; +} - return true; + +bool Expr::EvaluateCharRangeAsString(std::string &Result, + const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + EvalResult &Status) const { + return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, PtrExpression, Ctx, Status); +} + +bool Expr::EvaluateCharRangeAsString(APValue &Result, + const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + EvalResult &Status) const { + return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, PtrExpression, Ctx, Status); } bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const { diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index c8ff2ecea20cc..6e0d95714b641 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -455,11 +455,11 @@ std::string AsmStmt::generateAsmString(const ASTContext &C) const { llvm_unreachable("unknown asm statement kind!"); } -StringRef AsmStmt::getOutputConstraint(unsigned i) const { +std::string AsmStmt::getOutputConstraint(unsigned i) const { if (const auto *gccAsmStmt = dyn_cast(this)) return gccAsmStmt->getOutputConstraint(i); if (const auto *msAsmStmt = dyn_cast(this)) - return msAsmStmt->getOutputConstraint(i); + return msAsmStmt->getOutputConstraint(i).str(); llvm_unreachable("unknown asm statement kind!"); } @@ -471,11 +471,11 @@ const Expr *AsmStmt::getOutputExpr(unsigned i) const { llvm_unreachable("unknown asm statement kind!"); } -StringRef AsmStmt::getInputConstraint(unsigned i) const { +std::string AsmStmt::getInputConstraint(unsigned i) const { if (const auto *gccAsmStmt = dyn_cast(this)) return gccAsmStmt->getInputConstraint(i); if (const auto *msAsmStmt = dyn_cast(this)) - return msAsmStmt->getInputConstraint(i); + return msAsmStmt->getInputConstraint(i).str(); llvm_unreachable("unknown asm statement kind!"); } @@ -487,11 +487,11 @@ const Expr *AsmStmt::getInputExpr(unsigned i) const { llvm_unreachable("unknown asm statement kind!"); } -StringRef AsmStmt::getClobber(unsigned i) const { +std::string AsmStmt::getClobber(unsigned i) const { if (const auto *gccAsmStmt = dyn_cast(this)) return gccAsmStmt->getClobber(i); if (const auto *msAsmStmt = dyn_cast(this)) - return msAsmStmt->getClobber(i); + return msAsmStmt->getClobber(i).str(); llvm_unreachable("unknown asm statement kind!"); } @@ -510,8 +510,31 @@ char GCCAsmStmt::AsmStringPiece::getModifier() const { return isLetter(Str[0]) ? Str[0] : '\0'; } -StringRef GCCAsmStmt::getClobber(unsigned i) const { - return getClobberStringLiteral(i)->getString(); +std::string GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(const Expr* E) { + if(auto* SL = llvm::dyn_cast(E)) + return SL->getString().str(); + assert(E->getDependence() == ExprDependence::None && "cannot extract a string from a dependent expression"); + auto* CE = cast(E); + APValue Res = CE->getAPValueResult(); + assert(Res.isArray() && "expected an array"); + + std::string Out; + Out.reserve(Res.getArraySize()); + for(unsigned I = 0; I < Res.getArraySize(); ++I) { + APValue C = Res.getArrayInitializedElt(I); + assert(C.isInt()); + auto Ch = static_cast(C.getInt().getExtValue()); + Out.push_back(Ch); + } + return Out; +} + +std::string GCCAsmStmt::getAsmString() const { + return ExtractStringFromGCCAsmStmtComponent(getAsmStringExpr()); +} + +std::string GCCAsmStmt::getClobber(unsigned i) const { + return ExtractStringFromGCCAsmStmtComponent(getClobberExpr(i)); } Expr *GCCAsmStmt::getOutputExpr(unsigned i) { @@ -521,8 +544,8 @@ Expr *GCCAsmStmt::getOutputExpr(unsigned i) { /// getOutputConstraint - Return the constraint string for the specified /// output operand. All output constraints are known to be non-empty (either /// '=' or '+'). -StringRef GCCAsmStmt::getOutputConstraint(unsigned i) const { - return getOutputConstraintLiteral(i)->getString(); +std::string GCCAsmStmt::getOutputConstraint(unsigned i) const { + return ExtractStringFromGCCAsmStmtComponent(getOutputConstraintExpr(i)); } Expr *GCCAsmStmt::getInputExpr(unsigned i) { @@ -543,18 +566,18 @@ StringRef GCCAsmStmt::getLabelName(unsigned i) const { /// getInputConstraint - Return the specified input constraint. Unlike output /// constraints, these can be empty. -StringRef GCCAsmStmt::getInputConstraint(unsigned i) const { - return getInputConstraintLiteral(i)->getString(); +std::string GCCAsmStmt::getInputConstraint(unsigned i) const { + return ExtractStringFromGCCAsmStmtComponent(getInputConstraintExpr(i)); } void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C, IdentifierInfo **Names, - StringLiteral **Constraints, + Expr **Constraints, Stmt **Exprs, unsigned NumOutputs, unsigned NumInputs, unsigned NumLabels, - StringLiteral **Clobbers, + Expr **Clobbers, unsigned NumClobbers) { this->NumOutputs = NumOutputs; this->NumInputs = NumInputs; @@ -573,11 +596,11 @@ void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C, unsigned NumConstraints = NumOutputs + NumInputs; C.Deallocate(this->Constraints); - this->Constraints = new (C) StringLiteral*[NumConstraints]; + this->Constraints = new (C) Expr*[NumConstraints]; std::copy(Constraints, Constraints + NumConstraints, this->Constraints); C.Deallocate(this->Clobbers); - this->Clobbers = new (C) StringLiteral*[NumClobbers]; + this->Clobbers = new (C) Expr*[NumClobbers]; std::copy(Clobbers, Clobbers + NumClobbers, this->Clobbers); } @@ -609,9 +632,10 @@ int GCCAsmStmt::getNamedOperand(StringRef SymbolicName) const { /// true, otherwise return false. unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl&Pieces, const ASTContext &C, unsigned &DiagOffs) const { - StringRef Str = getAsmString()->getString(); - const char *StrStart = Str.begin(); - const char *StrEnd = Str.end(); + + std::string Str = getAsmString(); + const char *StrStart = Str.data(); + const char *StrEnd = Str.data() + Str.size(); const char *CurPtr = StrStart; // "Simple" inline asms have no constraints or operands, just convert the asm @@ -739,15 +763,21 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl&Pieces, // Str contains "x4" (Operand without the leading %). std::string Str(Begin, CurPtr - Begin); - // (BeginLoc, EndLoc) represents the range of the operand we are currently // processing. Unlike Str, the range includes the leading '%'. - SourceLocation BeginLoc = getAsmString()->getLocationOfByte( - Percent - StrStart, SM, LO, TI, &LastAsmStringToken, - &LastAsmStringOffset); - SourceLocation EndLoc = getAsmString()->getLocationOfByte( - CurPtr - StrStart, SM, LO, TI, &LastAsmStringToken, - &LastAsmStringOffset); + SourceLocation BeginLoc, EndLoc; + if(auto* SL = dyn_cast(getAsmStringExpr())) { + BeginLoc = SL->getLocationOfByte( + Percent - StrStart, SM, LO, TI, &LastAsmStringToken, + &LastAsmStringOffset); + EndLoc = SL->getLocationOfByte( + CurPtr - StrStart, SM, LO, TI, &LastAsmStringToken, + &LastAsmStringOffset); + } + else { + BeginLoc = getAsmStringExpr()->getBeginLoc(); + EndLoc = getAsmStringExpr()->getEndLoc(); + } Pieces.emplace_back(N, std::move(Str), BeginLoc, EndLoc); continue; @@ -778,12 +808,19 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl&Pieces, // (BeginLoc, EndLoc) represents the range of the operand we are currently // processing. Unlike Str, the range includes the leading '%'. - SourceLocation BeginLoc = getAsmString()->getLocationOfByte( + SourceLocation BeginLoc, EndLoc; + if(auto* SL = dyn_cast(getAsmStringExpr())) { + BeginLoc = SL->getLocationOfByte( Percent - StrStart, SM, LO, TI, &LastAsmStringToken, &LastAsmStringOffset); - SourceLocation EndLoc = getAsmString()->getLocationOfByte( + EndLoc = SL->getLocationOfByte( NameEnd + 1 - StrStart, SM, LO, TI, &LastAsmStringToken, &LastAsmStringOffset); + } + else { + BeginLoc = getAsmStringExpr()->getBeginLoc(); + EndLoc = getAsmStringExpr()->getEndLoc(); + } Pieces.emplace_back(N, std::move(Str), BeginLoc, EndLoc); @@ -863,9 +900,9 @@ void MSAsmStmt::setInputExpr(unsigned i, Expr *E) { GCCAsmStmt::GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple, bool isvolatile, unsigned numoutputs, unsigned numinputs, IdentifierInfo **names, - StringLiteral **constraints, Expr **exprs, - StringLiteral *asmstr, unsigned numclobbers, - StringLiteral **clobbers, unsigned numlabels, + Expr **constraints, Expr **exprs, + Expr *asmstr, unsigned numclobbers, + Expr **clobbers, unsigned numlabels, SourceLocation rparenloc) : AsmStmt(GCCAsmStmtClass, asmloc, issimple, isvolatile, numoutputs, numinputs, numclobbers), @@ -879,10 +916,10 @@ GCCAsmStmt::GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, std::copy(exprs, exprs + NumExprs, Exprs); unsigned NumConstraints = NumOutputs + NumInputs; - Constraints = new (C) StringLiteral*[NumConstraints]; + Constraints = new (C) Expr*[NumConstraints]; std::copy(constraints, constraints + NumConstraints, Constraints); - Clobbers = new (C) StringLiteral*[NumClobbers]; + Clobbers = new (C) Expr*[NumClobbers]; std::copy(clobbers, clobbers + NumClobbers, Clobbers); } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index e0063ec5f25eb..c8623225b486c 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -497,6 +497,19 @@ void StmtPrinter::VisitReturnStmt(ReturnStmt *Node) { if (Policy.IncludeNewlines) OS << NL; } +static void PrintGCCAsmString(raw_ostream &OS, Expr*E) { + if(E->getDependence()) { + OS << "<>"; + return; + } + + std::string Str = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(E); + llvm::SmallVector Out; + // FIXME: Unify with StringLiteral::outputString + EscapeStringForDiagnostic(Str, Out); + OS << '"' << Out << '"'; +} + void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { Indent() << "asm "; @@ -507,7 +520,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { OS << "goto "; OS << "("; - VisitStringLiteral(Node->getAsmString()); + PrintGCCAsmString(OS, Node->getAsmStringExpr()); // Outputs if (Node->getNumOutputs() != 0 || Node->getNumInputs() != 0 || @@ -524,7 +537,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { OS << "] "; } - VisitStringLiteral(Node->getOutputConstraintLiteral(i)); + PrintGCCAsmString(OS, Node->getOutputConstraintExpr(i)); OS << " ("; Visit(Node->getOutputExpr(i)); OS << ")"; @@ -545,7 +558,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { OS << "] "; } - VisitStringLiteral(Node->getInputConstraintLiteral(i)); + PrintGCCAsmString(OS, Node->getInputConstraintExpr(i)); OS << " ("; Visit(Node->getInputExpr(i)); OS << ")"; @@ -559,7 +572,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { if (i != 0) OS << ", "; - VisitStringLiteral(Node->getClobberStringLiteral(i)); + PrintGCCAsmString(OS, Node->getClobberExpr(i)); } // Labels diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index bf72de3854f4b..96852858b3b9e 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -328,20 +328,20 @@ void StmtProfiler::VisitGCCAsmStmt(const GCCAsmStmt *S) { VisitStmt(S); ID.AddBoolean(S->isVolatile()); ID.AddBoolean(S->isSimple()); - VisitStringLiteral(S->getAsmString()); + VisitExpr(S->getAsmStringExpr()); ID.AddInteger(S->getNumOutputs()); for (unsigned I = 0, N = S->getNumOutputs(); I != N; ++I) { ID.AddString(S->getOutputName(I)); - VisitStringLiteral(S->getOutputConstraintLiteral(I)); + VisitExpr(S->getOutputConstraintExpr(I)); } ID.AddInteger(S->getNumInputs()); for (unsigned I = 0, N = S->getNumInputs(); I != N; ++I) { ID.AddString(S->getInputName(I)); - VisitStringLiteral(S->getInputConstraintLiteral(I)); + VisitExpr(S->getInputConstraintExpr(I)); } ID.AddInteger(S->getNumClobbers()); for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I) - VisitStringLiteral(S->getClobberStringLiteral(I)); + VisitExpr(S->getClobberExpr(I)); ID.AddInteger(S->getNumLabels()); for (auto *L : S->labels()) VisitDecl(L->getLabel()); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 99b6f563d7c82..6ef9625f75118 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -2586,9 +2586,11 @@ static void UpdateAsmCallInst(llvm::CallBase &Result, bool HasSideEffect, // Slap the source location of the inline asm into a !srcloc metadata on the // call. - if (const auto *gccAsmStmt = dyn_cast(&S)) - Result.setMetadata("srcloc", - getAsmSrcLocInfo(gccAsmStmt->getAsmString(), CGF)); + if (const auto *gccAsmStmt = dyn_cast(&S)) { + if(const StringLiteral* SL = dyn_cast(gccAsmStmt->getAsmStringExpr())) + Result.setMetadata("srcloc", + getAsmSrcLocInfo(SL, CGF)); + } else { // At least put the line number on MS inline asm blobs. llvm::Constant *Loc = @@ -2705,9 +2707,9 @@ static void EmitHipStdParUnsupportedAsm(CodeGenFunction *CGF, const AsmStmt &S) { constexpr auto Name = "__ASM__hipstdpar_unsupported"; - StringRef Asm; + std::string Asm; if (auto GCCAsm = dyn_cast(&S)) - Asm = GCCAsm->getAsmString()->getString(); + Asm = GCCAsm->getAsmString(); auto &Ctx = CGF->CGM.getLLVMContext(); @@ -3050,7 +3052,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { // Clobbers for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) { - StringRef Clobber = S.getClobber(i); + std::string Clobber = S.getClobber(i); if (Clobber == "memory") ReadOnly = ReadNone = false; @@ -3071,7 +3073,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { if (Constraints.find("=&A") != std::string::npos) continue; std::string::size_type position1 = - Constraints.find("={" + Clobber.str() + "}"); + Constraints.find("={" + Clobber + "}"); if (position1 != std::string::npos) { Constraints.insert(position1 + 1, "&"); continue; diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp index 04c3a8700c10c..94144225ff406 100644 --- a/clang/lib/Parse/ParseStmtAsm.cpp +++ b/clang/lib/Parse/ParseStmtAsm.cpp @@ -809,7 +809,7 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { ConsumeToken(); } // Parse the asm-string list for clobbers if present. - if (!AteExtraColon && isTokenStringLiteral()) { + if (!AteExtraColon && (isTokenStringLiteral() || Tok.is(tok::l_paren))) { while (true) { ExprResult Clobber(ParseAsmStringLiteral(/*ForAsmLabel*/ false)); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 0710542f5e938..baf264c808820 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1668,28 +1668,37 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { /// string-literal /// ExprResult Parser::ParseAsmStringLiteral(bool ForAsmLabel) { - if (!isTokenStringLiteral()) { - Diag(Tok, diag::err_expected_string_literal) - << /*Source='in...'*/0 << "'asm'"; - return ExprError(); - } - ExprResult AsmString(ParseStringLiteralExpression()); - if (!AsmString.isInvalid()) { + ExprResult AsmString; + if(isTokenStringLiteral()) { + AsmString = ParseStringLiteralExpression(); + if(AsmString.isInvalid()) + return AsmString; + const auto *SL = cast(AsmString.get()); if (!SL->isOrdinary()) { Diag(Tok, diag::err_asm_operand_wide_string_literal) - << SL->isWide() - << SL->getSourceRange(); + << SL->isWide() + << SL->getSourceRange(); return ExprError(); } - if (ForAsmLabel && SL->getString().empty()) { - Diag(Tok, diag::err_asm_operand_wide_string_literal) - << 2 /* an empty */ << SL->getSourceRange(); + } + else if(!ForAsmLabel && getLangOpts().CPlusPlus11 && Tok.is(tok::l_paren)) { + ParenParseOption ExprType = SimpleExpr; + SourceLocation RParenLoc; + ParsedType CastTy; + + AsmString = ParseParenExpression(ExprType, true/*stopIfCastExpr*/, + false, CastTy, RParenLoc); + if(!AsmString.isUsable()) return ExprError(); - } } - return AsmString; + else { + Diag(Tok, diag::err_asm_expected_string) + << /*and expression=*/(getLangOpts().CPlusPlus11? 0 : 1); + } + + return Actions.ActOnGCCAsmStmtString(AsmString.get(), ForAsmLabel); } /// ParseSimpleAsm diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 96aac7871db1e..332099bbbd437 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17261,17 +17261,34 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { } } -bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, - std::string &Result, - ASTContext &Ctx, - bool ErrorOnInvalidMessage) { +template +static bool EvaluateAsStringImpl(Sema & SemaRef, + Expr *Message, + ResultType &Result, ASTContext &Ctx, + Sema::StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage) { + assert(Message); assert(!Message->isTypeDependent() && !Message->isValueDependent() && "can't evaluate a dependant static assert message"); if (const auto *SL = dyn_cast(Message)) { assert(SL->isUnevaluated() && "expected an unevaluated string"); - Result.assign(SL->getString().begin(), SL->getString().end()); + if constexpr(std::is_same_v) { + Result = APValue(APValue::UninitArray{}, SL->getLength(), SL->getLength()); + const ConstantArrayType *CAT = SemaRef.getASTContext().getAsConstantArrayType(SL->getType()); + assert(CAT && "string literal isn't an array"); + QualType CharType = CAT->getElementType(); + llvm::APSInt Value(SemaRef.getASTContext().getTypeSize(CharType), + CharType->isUnsignedIntegerType()); + for(unsigned I = 0; I < SL->getLength(); I++) { + Value = SL->getCodeUnit(I); + Result.getArrayInitializedElt(I) = APValue(Value); + } + } + else { + Result.assign(SL->getString().begin(), SL->getString().end()); + } return true; } @@ -17279,15 +17296,15 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, QualType T = Message->getType().getNonReferenceType(); auto *RD = T->getAsCXXRecordDecl(); if (!RD) { - Diag(Loc, diag::err_static_assert_invalid_message); + SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid) << EvalContext; return false; } auto FindMember = [&](StringRef Member, bool &Empty, bool Diag = false) -> std::optional { - DeclarationName DN = PP.getIdentifierInfo(Member); - LookupResult MemberLookup(*this, DN, Loc, Sema::LookupMemberName); - LookupQualifiedName(MemberLookup, RD); + DeclarationName DN = SemaRef.PP.getIdentifierInfo(Member); + LookupResult MemberLookup(SemaRef, DN, Loc, Sema::LookupMemberName); + SemaRef.LookupQualifiedName(MemberLookup, RD); Empty = MemberLookup.empty(); OverloadCandidateSet Candidates(MemberLookup.getNameLoc(), OverloadCandidateSet::CSK_Normal); @@ -17300,7 +17317,8 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, std::optional SizeMember = FindMember("size", SizeNotFound); std::optional DataMember = FindMember("data", DataNotFound); if (SizeNotFound || DataNotFound) { - Diag(Loc, diag::err_static_assert_missing_member_function) + SemaRef.Diag(Loc, diag::err_user_defined_msg_missing_member_function) + << EvalContext << ((SizeNotFound && DataNotFound) ? 2 : SizeNotFound ? 0 : 1); @@ -17316,47 +17334,49 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, } auto BuildExpr = [&](LookupResult &LR) { - ExprResult Res = BuildMemberReferenceExpr( + ExprResult Res = SemaRef.BuildMemberReferenceExpr( Message, Message->getType(), Message->getBeginLoc(), false, CXXScopeSpec(), SourceLocation(), nullptr, LR, nullptr, nullptr); if (Res.isInvalid()) return ExprError(); - Res = BuildCallExpr(nullptr, Res.get(), Loc, {}, Loc, nullptr, false, true); + Res = SemaRef.BuildCallExpr(nullptr, Res.get(), Loc, {}, Loc, nullptr, false, true); if (Res.isInvalid()) return ExprError(); if (Res.get()->isTypeDependent() || Res.get()->isValueDependent()) return ExprError(); - return TemporaryMaterializationConversion(Res.get()); + return SemaRef.TemporaryMaterializationConversion(Res.get()); }; ExprResult SizeE = BuildExpr(*SizeMember); ExprResult DataE = BuildExpr(*DataMember); - QualType SizeT = Context.getSizeType(); + QualType SizeT = SemaRef.Context.getSizeType(); QualType ConstCharPtr = - Context.getPointerType(Context.getConstType(Context.CharTy)); + SemaRef.Context.getPointerType(SemaRef.Context.getConstType(SemaRef.Context.CharTy)); ExprResult EvaluatedSize = SizeE.isInvalid() ? ExprError() - : BuildConvertedConstantExpression( - SizeE.get(), SizeT, CCEK_StaticAssertMessageSize); + : SemaRef.BuildConvertedConstantExpression( + SizeE.get(), SizeT, Sema::CCEK_StaticAssertMessageSize); if (EvaluatedSize.isInvalid()) { - Diag(Loc, diag::err_static_assert_invalid_mem_fn_ret_ty) << /*size*/ 0; + SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid_mem_fn_ret_ty) + << EvalContext << /*size*/ 0; return false; } ExprResult EvaluatedData = DataE.isInvalid() ? ExprError() - : BuildConvertedConstantExpression(DataE.get(), ConstCharPtr, - CCEK_StaticAssertMessageData); + : SemaRef.BuildConvertedConstantExpression(DataE.get(), ConstCharPtr, + Sema::CCEK_StaticAssertMessageData); if (EvaluatedData.isInvalid()) { - Diag(Loc, diag::err_static_assert_invalid_mem_fn_ret_ty) << /*data*/ 1; + SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid_mem_fn_ret_ty) + << EvalContext << /*data*/ 1; return false; } if (!ErrorOnInvalidMessage && - Diags.isIgnored(diag::warn_static_assert_message_constexpr, Loc)) + SemaRef.Diags.isIgnored(diag::warn_user_defined_msg_constexpr, Loc)) return true; Expr::EvalResult Status; @@ -17365,16 +17385,30 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, if (!Message->EvaluateCharRangeAsString(Result, EvaluatedSize.get(), EvaluatedData.get(), Ctx, Status) || !Notes.empty()) { - Diag(Message->getBeginLoc(), - ErrorOnInvalidMessage ? diag::err_static_assert_message_constexpr - : diag::warn_static_assert_message_constexpr); + SemaRef.Diag(Message->getBeginLoc(), + ErrorOnInvalidMessage ? diag::err_user_defined_msg_constexpr + : diag::warn_user_defined_msg_constexpr) + << EvalContext; for (const auto &Note : Notes) - Diag(Note.first, Note.second); + SemaRef.Diag(Note.first, Note.second); return !ErrorOnInvalidMessage; } return true; } +bool Sema::EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx, + StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage) { + return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext, ErrorOnInvalidMessage); + +} + +bool Sema::EvaluateAsString(Expr *Message, std::string &Result, ASTContext &Ctx, + StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage) { + return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext, ErrorOnInvalidMessage); +} + Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, Expr *AssertExpr, Expr *AssertMessage, SourceLocation RParenLoc, @@ -17420,8 +17454,9 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, // the message is grammatically valid without evaluating it. if (!Failed && AssertMessage && Cond.getBoolValue()) { std::string Str; - EvaluateStaticAssertMessageAsString(AssertMessage, Str, Context, - /*ErrorOnInvalidMessage=*/false); + EvaluateAsString(AssertMessage, Str, Context, + StringEvaluationContext::StaticAssert, + /*ErrorOnInvalidMessage=*/false); } // CWG2518 @@ -17436,10 +17471,10 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, bool HasMessage = AssertMessage; if (AssertMessage) { std::string Str; - HasMessage = - EvaluateStaticAssertMessageAsString( - AssertMessage, Str, Context, /*ErrorOnInvalidMessage=*/true) || - !Str.empty(); + HasMessage = EvaluateAsString(AssertMessage, Str, Context, + StringEvaluationContext::StaticAssert, + /*ErrorOnInvalidMessage=*/true) || + !Str.empty(); Msg << Str; } Expr *InnerCond = nullptr; diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp index a0b203fbdfec2..e168f57f8f303 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -17,6 +17,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Ownership.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "llvm/ADT/ArrayRef.h" @@ -203,15 +204,15 @@ static StringRef extractRegisterName(const Expr *Expression, // clobbers list. If there's a conflict, returns the location of the // conflicted clobber, else returns nullptr static SourceLocation -getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints, - StringLiteral **Clobbers, int NumClobbers, +getClobberConflictLocation(MultiExprArg Exprs, Expr **Constraints, + Expr **Clobbers, int NumClobbers, unsigned NumLabels, const TargetInfo &Target, ASTContext &Cont) { llvm::StringSet<> InOutVars; // Collect all the input and output registers from the extended asm // statement in order to check for conflicts with the clobber list for (unsigned int i = 0; i < Exprs.size() - NumLabels; ++i) { - StringRef Constraint = Constraints[i]->getString(); + std::string Constraint = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraints[i]); StringRef InOutReg = Target.getConstraintRegister( Constraint, extractRegisterName(Exprs[i], Target)); if (InOutReg != "") @@ -220,7 +221,7 @@ getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints, // Check for each item in the clobber list if it conflicts with the input // or output for (int i = 0; i < NumClobbers; ++i) { - StringRef Clobber = Clobbers[i]->getString(); + std::string Clobber = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Clobbers[i]); // We only check registers, therefore we don't check cc and memory // clobbers if (Clobber == "cc" || Clobber == "memory" || Clobber == "unwind") @@ -233,6 +234,36 @@ getClobberConflictLocation(MultiExprArg Exprs, StringLiteral **Constraints, return SourceLocation(); } +ExprResult Sema::ActOnGCCAsmStmtString(Expr* Expr, bool ForAsmLabel) { + if(!Expr) + return ExprError(); + + if(auto* SL = dyn_cast(Expr)) { + assert(SL->isOrdinary()); + if (ForAsmLabel && SL->getString().empty()) { + Diag(Expr->getBeginLoc(), diag::err_asm_operand_empty_string) + << SL->getSourceRange(); + } + return SL; + } + if(DiagnoseUnexpandedParameterPack(Expr)) + return ExprError(); + if(Expr->getDependence() != ExprDependence::None) + return Expr; + APValue V; + if(!EvaluateAsString(Expr, V, getASTContext(), StringEvaluationContext::Asm, + /*ErrorOnInvalid=*/true)) + return ExprError(); + + if (ForAsmLabel && V.getArrayInitializedElts() == 0) { + Diag(Expr->getBeginLoc(), diag::err_asm_operand_empty_string); + } + + ConstantExpr* Res = ConstantExpr::Create(getASTContext(), Expr, ConstantResultStorageKind::APValue); + Res->SetResult(V, getASTContext()); + return Res; +} + StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, unsigned NumOutputs, unsigned NumInputs, IdentifierInfo **Names, @@ -241,38 +272,44 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned NumLabels, SourceLocation RParenLoc) { unsigned NumClobbers = clobbers.size(); - StringLiteral **Constraints = - reinterpret_cast(constraints.data()); - StringLiteral *AsmString = cast(asmString); - StringLiteral **Clobbers = reinterpret_cast(clobbers.data()); SmallVector OutputConstraintInfos; - // The parser verifies that there is a string literal here. - assert(AsmString->isOrdinary()); - FunctionDecl *FD = dyn_cast(getCurLexicalContext()); llvm::StringMap FeatureMap; Context.getFunctionFeatureMap(FeatureMap, FD); - for (unsigned i = 0; i != NumOutputs; i++) { - StringLiteral *Literal = Constraints[i]; - assert(Literal->isOrdinary()); + auto CreateGCCAsmStmt = [&] { + return new (Context) + GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, + NumInputs, Names, constraints.data(), Exprs.data(), asmString, + NumClobbers, clobbers.data(), NumLabels, RParenLoc); + }; + + if(asmString->getDependence() != ExprDependence::None + || llvm::any_of(constraints, [] (Expr* E) { + return E->getDependence() != ExprDependence::None; + }) + || llvm::any_of(clobbers, [] (Expr* E) { + return E->getDependence() != ExprDependence::None; + })) + return CreateGCCAsmStmt(); + for (unsigned i = 0; i != NumOutputs; i++) { + Expr* Constraint = constraints[i]; StringRef OutputName; if (Names[i]) OutputName = Names[i]->getName(); - TargetInfo::ConstraintInfo Info(Literal->getString(), OutputName); + std::string ConstraintStr = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint); + + TargetInfo::ConstraintInfo Info(ConstraintStr, OutputName); if (!Context.getTargetInfo().validateOutputConstraint(Info) && !(LangOpts.HIPStdPar && LangOpts.CUDAIsDevice)) { - targetDiag(Literal->getBeginLoc(), + targetDiag(Constraint->getBeginLoc(), diag::err_asm_invalid_output_constraint) << Info.getConstraintStr(); - return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), AsmString, - NumClobbers, Clobbers, NumLabels, RParenLoc); + return CreateGCCAsmStmt(); } ExprResult ER = CheckPlaceholderExpr(Exprs[i]); @@ -335,35 +372,30 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned Size = Context.getTypeSize(OutputExpr->getType()); if (!Context.getTargetInfo().validateOutputSize( - FeatureMap, Literal->getString(), Size)) { + FeatureMap, GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint), Size)) { targetDiag(OutputExpr->getBeginLoc(), diag::err_asm_invalid_output_size) << Info.getConstraintStr(); - return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), AsmString, - NumClobbers, Clobbers, NumLabels, RParenLoc); + return CreateGCCAsmStmt(); } } SmallVector InputConstraintInfos; for (unsigned i = NumOutputs, e = NumOutputs + NumInputs; i != e; i++) { - StringLiteral *Literal = Constraints[i]; - assert(Literal->isOrdinary()); + Expr *Constraint = constraints[i]; StringRef InputName; if (Names[i]) InputName = Names[i]->getName(); - TargetInfo::ConstraintInfo Info(Literal->getString(), InputName); + std::string ConstraintStr = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint); + + TargetInfo::ConstraintInfo Info(ConstraintStr, InputName); if (!Context.getTargetInfo().validateInputConstraint(OutputConstraintInfos, Info)) { - targetDiag(Literal->getBeginLoc(), diag::err_asm_invalid_input_constraint) + targetDiag(Constraint->getBeginLoc(), diag::err_asm_invalid_input_constraint) << Info.getConstraintStr(); - return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), AsmString, - NumClobbers, Clobbers, NumLabels, RParenLoc); + return CreateGCCAsmStmt(); } ExprResult ER = CheckPlaceholderExpr(Exprs[i]); @@ -449,7 +481,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned Size = Context.getTypeSize(Ty); if (!Context.getTargetInfo().validateInputSize(FeatureMap, - Literal->getString(), Size)) + ConstraintStr, Size)) return targetDiag(InputExpr->getBeginLoc(), diag::err_asm_invalid_input_size) << Info.getConstraintStr(); @@ -459,46 +491,45 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, // Check that the clobbers are valid. for (unsigned i = 0; i != NumClobbers; i++) { - StringLiteral *Literal = Clobbers[i]; - assert(Literal->isOrdinary()); + Expr *ClobberExpr = clobbers[i]; - StringRef Clobber = Literal->getString(); + std::string Clobber = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(ClobberExpr); if (!Context.getTargetInfo().isValidClobber(Clobber)) { - targetDiag(Literal->getBeginLoc(), diag::err_asm_unknown_register_name) + targetDiag(ClobberExpr->getBeginLoc(), diag::err_asm_unknown_register_name) << Clobber; return new (Context) GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), AsmString, - NumClobbers, Clobbers, NumLabels, RParenLoc); + NumInputs, Names, constraints.data(), Exprs.data(), asmString, + NumClobbers, clobbers.data(), NumLabels, RParenLoc); } if (Clobber == "unwind") { - UnwindClobberLoc = Literal->getBeginLoc(); + UnwindClobberLoc = ClobberExpr->getBeginLoc(); } } // Using unwind clobber and asm-goto together is not supported right now. if (UnwindClobberLoc && NumLabels > 0) { targetDiag(*UnwindClobberLoc, diag::err_asm_unwind_and_goto); - return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, NumInputs, - Names, Constraints, Exprs.data(), AsmString, NumClobbers, - Clobbers, NumLabels, RParenLoc); + return CreateGCCAsmStmt(); } - GCCAsmStmt *NS = - new (Context) GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, Constraints, Exprs.data(), - AsmString, NumClobbers, Clobbers, NumLabels, - RParenLoc); + GCCAsmStmt *NS = CreateGCCAsmStmt(); // Validate the asm string, ensuring it makes sense given the operands we // have. + + auto GetLocation = [this](const Expr* Str, unsigned Offset) { + if(auto* SL = dyn_cast(Str)) + return getLocationOfStringLiteralByte(SL, Offset); + return Str->getBeginLoc(); + }; + SmallVector Pieces; unsigned DiagOffs; if (unsigned DiagID = NS->AnalyzeAsmString(Pieces, Context, DiagOffs)) { - targetDiag(getLocationOfStringLiteralByte(AsmString, DiagOffs), DiagID) - << AsmString->getSourceRange(); + targetDiag(GetLocation(asmString, DiagOffs), DiagID) + << asmString->getSourceRange(); return NS; } @@ -529,7 +560,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, } // Now that we have the right indexes go ahead and check. - StringLiteral *Literal = Constraints[ConstraintIdx]; + Expr *Constraint = constraints[ConstraintIdx]; const Type *Ty = Exprs[ConstraintIdx]->getType().getTypePtr(); if (Ty->isDependentType() || Ty->isIncompleteType()) continue; @@ -537,7 +568,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned Size = Context.getTypeSize(Ty); std::string SuggestedModifier; if (!Context.getTargetInfo().validateConstraintModifier( - Literal->getString(), Piece.getModifier(), Size, + GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint), Piece.getModifier(), Size, SuggestedModifier)) { targetDiag(Exprs[ConstraintIdx]->getBeginLoc(), diag::warn_asm_mismatched_size_modifier); @@ -546,8 +577,10 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, auto B = targetDiag(Piece.getRange().getBegin(), diag::note_asm_missing_constraint_modifier) << SuggestedModifier; - SuggestedModifier = "%" + SuggestedModifier + Piece.getString(); - B << FixItHint::CreateReplacement(Piece.getRange(), SuggestedModifier); + if(isa(Constraint)) { + SuggestedModifier = "%" + SuggestedModifier + Piece.getString(); + B << FixItHint::CreateReplacement(Piece.getRange(), SuggestedModifier); + } } } } @@ -708,7 +741,7 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, // Check for conflicts between clobber list and input or output lists SourceLocation ConstraintLoc = - getClobberConflictLocation(Exprs, Constraints, Clobbers, NumClobbers, + getClobberConflictLocation(Exprs, constraints.data(), clobbers.data(), NumClobbers, NumLabels, Context.getTargetInfo(), Context); if (ConstraintLoc.isValid()) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index a09f623ff8fbf..06d41a8154ca3 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -8552,25 +8552,39 @@ template StmtResult TreeTransform::TransformGCCAsmStmt(GCCAsmStmt *S) { + SmallVector Constraints; SmallVector Exprs; SmallVector Names; - ExprResult AsmString; SmallVector Clobbers; bool ExprsChanged = false; + auto RebuildString = [&] (Expr* E) { + ExprResult Result = getDerived().TransformExpr(E); + if (Result.isInvalid()) + return Result; + if(!Result.isInvalid() && Result.get() != E) { + ExprsChanged = true; + Result = SemaRef.ActOnGCCAsmStmtString(Result.get(), /*ForLabel=*/false); + } + return Result; + }; + // Go through the outputs. for (unsigned I = 0, E = S->getNumOutputs(); I != E; ++I) { Names.push_back(S->getOutputIdentifier(I)); - // No need to transform the constraint literal. - Constraints.push_back(S->getOutputConstraintLiteral(I)); + ExprResult Result = RebuildString(S->getOutputConstraintExpr(I)); + if (Result.isInvalid()) + return StmtError(); + + Constraints.push_back(Result.get()); // Transform the output expr. Expr *OutputExpr = S->getOutputExpr(I); - ExprResult Result = getDerived().TransformExpr(OutputExpr); + Result = getDerived().TransformExpr(OutputExpr); if (Result.isInvalid()) return StmtError(); @@ -8583,12 +8597,15 @@ TreeTransform::TransformGCCAsmStmt(GCCAsmStmt *S) { for (unsigned I = 0, E = S->getNumInputs(); I != E; ++I) { Names.push_back(S->getInputIdentifier(I)); - // No need to transform the constraint literal. - Constraints.push_back(S->getInputConstraintLiteral(I)); + ExprResult Result = RebuildString(S->getInputConstraintExpr(I)); + if (Result.isInvalid()) + return StmtError(); + + Constraints.push_back(Result.get()); // Transform the input expr. Expr *InputExpr = S->getInputExpr(I); - ExprResult Result = getDerived().TransformExpr(InputExpr); + Result = getDerived().TransformExpr(InputExpr); if (Result.isInvalid()) return StmtError(); @@ -8607,15 +8624,22 @@ TreeTransform::TransformGCCAsmStmt(GCCAsmStmt *S) { ExprsChanged |= Result.get() != S->getLabelExpr(I); Exprs.push_back(Result.get()); } - if (!getDerived().AlwaysRebuild() && !ExprsChanged) - return S; // Go through the clobbers. - for (unsigned I = 0, E = S->getNumClobbers(); I != E; ++I) - Clobbers.push_back(S->getClobberStringLiteral(I)); + for (unsigned I = 0, E = S->getNumClobbers(); I != E; ++I) { + ExprResult Result = RebuildString(S->getClobberExpr(I)); + if (Result.isInvalid()) + return StmtError(); + Clobbers.push_back(Result.get()); + } + + ExprResult AsmString = RebuildString(S->getAsmStringExpr()); + if (AsmString.isInvalid()) + return StmtError(); + + if (!getDerived().AlwaysRebuild() && !ExprsChanged) + return S; - // No need to transform the asm string literal. - AsmString = S->getAsmString(); return getDerived().RebuildGCCAsmStmt(S->getAsmLoc(), S->isSimple(), S->isVolatile(), S->getNumOutputs(), S->getNumInputs(), Names.data(), diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 48f9f89bd6e4c..7ad1216cb53e2 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -381,7 +381,7 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { VisitAsmStmt(S); S->NumLabels = Record.readInt(); S->setRParenLoc(readSourceLocation()); - S->setAsmString(cast_or_null(Record.readSubStmt())); + S->setAsmString(cast_or_null(Record.readSubStmt())); unsigned NumOutputs = S->getNumOutputs(); unsigned NumInputs = S->getNumInputs(); @@ -390,18 +390,18 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { // Outputs and inputs SmallVector Names; - SmallVector Constraints; + SmallVector Constraints; SmallVector Exprs; for (unsigned I = 0, N = NumOutputs + NumInputs; I != N; ++I) { Names.push_back(Record.readIdentifier()); - Constraints.push_back(cast_or_null(Record.readSubStmt())); + Constraints.push_back(cast_or_null(Record.readSubStmt())); Exprs.push_back(Record.readSubStmt()); } // Constraints - SmallVector Clobbers; + SmallVector Clobbers; for (unsigned I = 0; I != NumClobbers; ++I) - Clobbers.push_back(cast_or_null(Record.readSubStmt())); + Clobbers.push_back(cast_or_null(Record.readSubStmt())); // Labels for (unsigned I = 0, N = NumLabels; I != N; ++I) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index aa5a7854394a0..32211ced4ff15 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -361,25 +361,25 @@ void ASTStmtWriter::VisitGCCAsmStmt(GCCAsmStmt *S) { VisitAsmStmt(S); Record.push_back(S->getNumLabels()); Record.AddSourceLocation(S->getRParenLoc()); - Record.AddStmt(S->getAsmString()); + Record.AddStmt(S->getAsmStringExpr()); // Outputs for (unsigned I = 0, N = S->getNumOutputs(); I != N; ++I) { Record.AddIdentifierRef(S->getOutputIdentifier(I)); - Record.AddStmt(S->getOutputConstraintLiteral(I)); + Record.AddStmt(S->getOutputConstraintExpr(I)); Record.AddStmt(S->getOutputExpr(I)); } // Inputs for (unsigned I = 0, N = S->getNumInputs(); I != N; ++I) { Record.AddIdentifierRef(S->getInputIdentifier(I)); - Record.AddStmt(S->getInputConstraintLiteral(I)); + Record.AddStmt(S->getInputConstraintExpr(I)); Record.AddStmt(S->getInputExpr(I)); } // Clobbers for (unsigned I = 0, N = S->getNumClobbers(); I != N; ++I) - Record.AddStmt(S->getClobberStringLiteral(I)); + Record.AddStmt(S->getClobberExpr(I)); // Labels for (unsigned I = 0, N = S->getNumLabels(); I != N; ++I) { From 6e535f3499d5ae3c9a0f906a418f8faa10975efd Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Wed, 12 Mar 2025 19:42:22 +0100 Subject: [PATCH 02/16] format --- clang/include/clang/AST/Expr.h | 7 +- clang/include/clang/AST/Stmt.h | 24 ++---- clang/include/clang/Sema/Sema.h | 13 +-- clang/lib/AST/ExprConstant.cpp | 85 +++++++++---------- clang/lib/AST/Stmt.cpp | 75 ++++++++--------- clang/lib/AST/StmtPrinter.cpp | 4 +- clang/lib/CodeGen/CGStmt.cpp | 9 +-- clang/lib/Parse/Parser.cpp | 27 ++++--- clang/lib/Sema/SemaDeclCXX.cpp | 75 ++++++++--------- clang/lib/Sema/SemaStmtAsm.cpp | 99 ++++++++++++----------- clang/lib/Sema/TreeTransform.h | 25 +++--- clang/lib/Serialization/ASTReaderStmt.cpp | 4 +- 12 files changed, 219 insertions(+), 228 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 7f9e50dfe78bd..28437b5629b00 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -787,10 +787,9 @@ class Expr : public ValueStmt { const Expr *PtrExpression, ASTContext &Ctx, EvalResult &Status) const; - bool EvaluateCharRangeAsString(APValue &Result, - const Expr *SizeExpression, - const Expr *PtrExpression, ASTContext &Ctx, - EvalResult &Status) const; + bool EvaluateCharRangeAsString(APValue &Result, const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + EvalResult &Status) const; /// If the current Expr can be evaluated to a pointer to a null-terminated /// constant string, return the constant string (without the terminating diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 7bf0a6911c515..88ae3aeefa869 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -3314,9 +3314,8 @@ class GCCAsmStmt : public AsmStmt { GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple, bool isvolatile, unsigned numoutputs, unsigned numinputs, IdentifierInfo **names, Expr **constraints, Expr **exprs, - Expr *asmstr, unsigned numclobbers, - Expr **clobbers, unsigned numlabels, - SourceLocation rparenloc); + Expr *asmstr, unsigned numclobbers, Expr **clobbers, + unsigned numlabels, SourceLocation rparenloc); /// Build an empty inline-assembly statement. explicit GCCAsmStmt(EmptyShell Empty) : AsmStmt(GCCAsmStmtClass, Empty) {} @@ -3404,9 +3403,7 @@ class GCCAsmStmt : public AsmStmt { const Expr *getOutputConstraintExpr(unsigned i) const { return Constraints[i]; } - Expr *getOutputConstraintExpr(unsigned i) { - return Constraints[i]; - } + Expr *getOutputConstraintExpr(unsigned i) { return Constraints[i]; } Expr *getOutputExpr(unsigned i); @@ -3443,7 +3440,7 @@ class GCCAsmStmt : public AsmStmt { return const_cast(this)->getInputExpr(i); } - static std::string ExtractStringFromGCCAsmStmtComponent(const Expr* E); + static std::string ExtractStringFromGCCAsmStmtComponent(const Expr *E); //===--- Labels ---===// @@ -3493,12 +3490,9 @@ class GCCAsmStmt : public AsmStmt { private: void setOutputsAndInputsAndClobbers(const ASTContext &C, IdentifierInfo **Names, - Expr **Constraints, - Stmt **Exprs, - unsigned NumOutputs, - unsigned NumInputs, - unsigned NumLabels, - Expr **Clobbers, + Expr **Constraints, Stmt **Exprs, + unsigned NumOutputs, unsigned NumInputs, + unsigned NumLabels, Expr **Clobbers, unsigned NumClobbers); public: @@ -3512,9 +3506,7 @@ class GCCAsmStmt : public AsmStmt { std::string getClobber(unsigned i) const; Expr *getClobberExpr(unsigned i) { return Clobbers[i]; } - const Expr *getClobberExpr(unsigned i) const { - return Clobbers[i]; - } + const Expr *getClobberExpr(unsigned i) const { return Clobbers[i]; } SourceLocation getBeginLoc() const LLVM_READONLY { return AsmLoc; } SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index e1b1083675612..4043699912423 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5585,10 +5585,7 @@ class Sema final : public SemaBase { void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method); void ActOnFinishDelayedMemberInitializers(Decl *Record); - enum class StringEvaluationContext { - StaticAssert = 0, - Asm = 1 - }; + enum class StringEvaluationContext { StaticAssert = 0, Asm = 1 }; bool EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx, StringEvaluationContext EvalContext, @@ -11049,7 +11046,7 @@ class Sema final : public SemaBase { ///@{ public: - ExprResult ActOnGCCAsmStmtString(Expr* Stm, bool ForAsmLabel); + ExprResult ActOnGCCAsmStmtString(Expr *Stm, bool ForAsmLabel); StmtResult ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, bool IsVolatile, unsigned NumOutputs, unsigned NumInputs, IdentifierInfo **Names, @@ -15339,14 +15336,12 @@ void Sema::PragmaStack::Act(SourceLocation PragmaLocation, llvm::StringRef StackSlotLabel, AlignPackInfo Value); -inline -const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, - Sema::StringEvaluationContext Ctx) { +inline const StreamingDiagnostic & +operator<<(const StreamingDiagnostic &DB, Sema::StringEvaluationContext Ctx) { DB << llvm::to_underlying(Ctx); return DB; } - } // end namespace clang #endif diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 86fe8554cba85..70f3531524620 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -17929,69 +17929,70 @@ std::optional Expr::tryEvaluateString(ASTContext &Ctx) const { } template -static bool EvaluateCharRangeAsStringImpl(const Expr*, T& Result, +static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, const Expr *SizeExpression, - const Expr *PtrExpression, ASTContext &Ctx, + const Expr *PtrExpression, + ASTContext &Ctx, Expr::EvalResult &Status) { - LValue String; - EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression); - Info.InConstantContext = true; - - FullExpressionRAII Scope(Info); - APSInt SizeValue; - if (!::EvaluateInteger(SizeExpression, SizeValue, Info)) - return false; + LValue String; + EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression); + Info.InConstantContext = true; - uint64_t Size = SizeValue.getZExtValue(); + FullExpressionRAII Scope(Info); + APSInt SizeValue; + if (!::EvaluateInteger(SizeExpression, SizeValue, Info)) + return false; - if constexpr(std::is_same_v) - Result = APValue(APValue::UninitArray{}, Size, Size); - //else - // Result.reserve(Size); + uint64_t Size = SizeValue.getZExtValue(); - if (!::EvaluatePointer(PtrExpression, String, Info)) - return false; + if constexpr (std::is_same_v) + Result = APValue(APValue::UninitArray{}, Size, Size); + // else + // Result.reserve(Size); - QualType CharTy = PtrExpression->getType()->getPointeeType(); - for (uint64_t I = 0; I < Size; ++I) { - APValue Char; - if (!handleLValueToRValueConversion(Info, PtrExpression, CharTy, String, - Char)) - return false; + if (!::EvaluatePointer(PtrExpression, String, Info)) + return false; - if constexpr(std::is_same_v) { - Result.getArrayInitializedElt(I) = std::move(Char); - } - else { - APSInt C = Char.getInt(); - Result.push_back(static_cast(C.getExtValue())); - } + QualType CharTy = PtrExpression->getType()->getPointeeType(); + for (uint64_t I = 0; I < Size; ++I) { + APValue Char; + if (!handleLValueToRValueConversion(Info, PtrExpression, CharTy, String, + Char)) + return false; - if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1)) - return false; + if constexpr (std::is_same_v) { + Result.getArrayInitializedElt(I) = std::move(Char); + } else { + APSInt C = Char.getInt(); + Result.push_back(static_cast(C.getExtValue())); } - if (!Scope.destroy()) - return false; - if (!CheckMemoryLeaks(Info)) + if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1)) return false; + } + if (!Scope.destroy()) + return false; - return true; -} + if (!CheckMemoryLeaks(Info)) + return false; + return true; +} bool Expr::EvaluateCharRangeAsString(std::string &Result, const Expr *SizeExpression, const Expr *PtrExpression, ASTContext &Ctx, EvalResult &Status) const { - return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, PtrExpression, Ctx, Status); + return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, + PtrExpression, Ctx, Status); } bool Expr::EvaluateCharRangeAsString(APValue &Result, - const Expr *SizeExpression, - const Expr *PtrExpression, ASTContext &Ctx, - EvalResult &Status) const { - return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, PtrExpression, Ctx, Status); + const Expr *SizeExpression, + const Expr *PtrExpression, ASTContext &Ctx, + EvalResult &Status) const { + return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, + PtrExpression, Ctx, Status); } bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const { diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index 6e0d95714b641..be4ba6878bf0c 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -510,17 +510,18 @@ char GCCAsmStmt::AsmStringPiece::getModifier() const { return isLetter(Str[0]) ? Str[0] : '\0'; } -std::string GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(const Expr* E) { - if(auto* SL = llvm::dyn_cast(E)) +std::string GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(const Expr *E) { + if (auto *SL = llvm::dyn_cast(E)) return SL->getString().str(); - assert(E->getDependence() == ExprDependence::None && "cannot extract a string from a dependent expression"); - auto* CE = cast(E); + assert(E->getDependence() == ExprDependence::None && + "cannot extract a string from a dependent expression"); + auto *CE = cast(E); APValue Res = CE->getAPValueResult(); assert(Res.isArray() && "expected an array"); std::string Out; Out.reserve(Res.getArraySize()); - for(unsigned I = 0; I < Res.getArraySize(); ++I) { + for (unsigned I = 0; I < Res.getArraySize(); ++I) { APValue C = Res.getArrayInitializedElt(I); assert(C.isInt()); auto Ch = static_cast(C.getInt().getExtValue()); @@ -570,15 +571,10 @@ std::string GCCAsmStmt::getInputConstraint(unsigned i) const { return ExtractStringFromGCCAsmStmtComponent(getInputConstraintExpr(i)); } -void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C, - IdentifierInfo **Names, - Expr **Constraints, - Stmt **Exprs, - unsigned NumOutputs, - unsigned NumInputs, - unsigned NumLabels, - Expr **Clobbers, - unsigned NumClobbers) { +void GCCAsmStmt::setOutputsAndInputsAndClobbers( + const ASTContext &C, IdentifierInfo **Names, Expr **Constraints, + Stmt **Exprs, unsigned NumOutputs, unsigned NumInputs, unsigned NumLabels, + Expr **Clobbers, unsigned NumClobbers) { this->NumOutputs = NumOutputs; this->NumInputs = NumInputs; this->NumClobbers = NumClobbers; @@ -596,11 +592,11 @@ void GCCAsmStmt::setOutputsAndInputsAndClobbers(const ASTContext &C, unsigned NumConstraints = NumOutputs + NumInputs; C.Deallocate(this->Constraints); - this->Constraints = new (C) Expr*[NumConstraints]; + this->Constraints = new (C) Expr *[NumConstraints]; std::copy(Constraints, Constraints + NumConstraints, this->Constraints); C.Deallocate(this->Clobbers); - this->Clobbers = new (C) Expr*[NumClobbers]; + this->Clobbers = new (C) Expr *[NumClobbers]; std::copy(Clobbers, Clobbers + NumClobbers, this->Clobbers); } @@ -766,15 +762,14 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl&Pieces, // (BeginLoc, EndLoc) represents the range of the operand we are currently // processing. Unlike Str, the range includes the leading '%'. SourceLocation BeginLoc, EndLoc; - if(auto* SL = dyn_cast(getAsmStringExpr())) { - BeginLoc = SL->getLocationOfByte( - Percent - StrStart, SM, LO, TI, &LastAsmStringToken, - &LastAsmStringOffset); - EndLoc = SL->getLocationOfByte( - CurPtr - StrStart, SM, LO, TI, &LastAsmStringToken, - &LastAsmStringOffset); - } - else { + if (auto *SL = dyn_cast(getAsmStringExpr())) { + BeginLoc = + SL->getLocationOfByte(Percent - StrStart, SM, LO, TI, + &LastAsmStringToken, &LastAsmStringOffset); + EndLoc = + SL->getLocationOfByte(CurPtr - StrStart, SM, LO, TI, + &LastAsmStringToken, &LastAsmStringOffset); + } else { BeginLoc = getAsmStringExpr()->getBeginLoc(); EndLoc = getAsmStringExpr()->getEndLoc(); } @@ -809,15 +804,14 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl&Pieces, // (BeginLoc, EndLoc) represents the range of the operand we are currently // processing. Unlike Str, the range includes the leading '%'. SourceLocation BeginLoc, EndLoc; - if(auto* SL = dyn_cast(getAsmStringExpr())) { - BeginLoc = SL->getLocationOfByte( - Percent - StrStart, SM, LO, TI, &LastAsmStringToken, - &LastAsmStringOffset); - EndLoc = SL->getLocationOfByte( - NameEnd + 1 - StrStart, SM, LO, TI, &LastAsmStringToken, - &LastAsmStringOffset); - } - else { + if (auto *SL = dyn_cast(getAsmStringExpr())) { + BeginLoc = + SL->getLocationOfByte(Percent - StrStart, SM, LO, TI, + &LastAsmStringToken, &LastAsmStringOffset); + EndLoc = + SL->getLocationOfByte(NameEnd + 1 - StrStart, SM, LO, TI, + &LastAsmStringToken, &LastAsmStringOffset); + } else { BeginLoc = getAsmStringExpr()->getBeginLoc(); EndLoc = getAsmStringExpr()->getEndLoc(); } @@ -900,13 +894,12 @@ void MSAsmStmt::setInputExpr(unsigned i, Expr *E) { GCCAsmStmt::GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, bool issimple, bool isvolatile, unsigned numoutputs, unsigned numinputs, IdentifierInfo **names, - Expr **constraints, Expr **exprs, - Expr *asmstr, unsigned numclobbers, - Expr **clobbers, unsigned numlabels, - SourceLocation rparenloc) + Expr **constraints, Expr **exprs, Expr *asmstr, + unsigned numclobbers, Expr **clobbers, + unsigned numlabels, SourceLocation rparenloc) : AsmStmt(GCCAsmStmtClass, asmloc, issimple, isvolatile, numoutputs, numinputs, numclobbers), - RParenLoc(rparenloc), AsmStr(asmstr), NumLabels(numlabels) { + RParenLoc(rparenloc), AsmStr(asmstr), NumLabels(numlabels) { unsigned NumExprs = NumOutputs + NumInputs + NumLabels; Names = new (C) IdentifierInfo*[NumExprs]; @@ -916,10 +909,10 @@ GCCAsmStmt::GCCAsmStmt(const ASTContext &C, SourceLocation asmloc, std::copy(exprs, exprs + NumExprs, Exprs); unsigned NumConstraints = NumOutputs + NumInputs; - Constraints = new (C) Expr*[NumConstraints]; + Constraints = new (C) Expr *[NumConstraints]; std::copy(constraints, constraints + NumConstraints, Constraints); - Clobbers = new (C) Expr*[NumClobbers]; + Clobbers = new (C) Expr *[NumClobbers]; std::copy(clobbers, clobbers + NumClobbers, Clobbers); } diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index c8623225b486c..bceab177a6aa3 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -497,8 +497,8 @@ void StmtPrinter::VisitReturnStmt(ReturnStmt *Node) { if (Policy.IncludeNewlines) OS << NL; } -static void PrintGCCAsmString(raw_ostream &OS, Expr*E) { - if(E->getDependence()) { +static void PrintGCCAsmString(raw_ostream &OS, Expr *E) { + if (E->getDependence()) { OS << "<>"; return; } diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 6ef9625f75118..c243d5c98239b 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -2587,11 +2587,10 @@ static void UpdateAsmCallInst(llvm::CallBase &Result, bool HasSideEffect, // Slap the source location of the inline asm into a !srcloc metadata on the // call. if (const auto *gccAsmStmt = dyn_cast(&S)) { - if(const StringLiteral* SL = dyn_cast(gccAsmStmt->getAsmStringExpr())) - Result.setMetadata("srcloc", - getAsmSrcLocInfo(SL, CGF)); - } - else { + if (const StringLiteral *SL = + dyn_cast(gccAsmStmt->getAsmStringExpr())) + Result.setMetadata("srcloc", getAsmSrcLocInfo(SL, CGF)); + } else { // At least put the line number on MS inline asm blobs. llvm::Constant *Loc = llvm::ConstantInt::get(CGF.Int64Ty, S.getAsmLoc().getRawEncoding()); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index baf264c808820..66d2e3238317e 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1670,32 +1670,33 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { ExprResult Parser::ParseAsmStringLiteral(bool ForAsmLabel) { ExprResult AsmString; - if(isTokenStringLiteral()) { + if (isTokenStringLiteral()) { AsmString = ParseStringLiteralExpression(); - if(AsmString.isInvalid()) + if (AsmString.isInvalid()) return AsmString; const auto *SL = cast(AsmString.get()); if (!SL->isOrdinary()) { Diag(Tok, diag::err_asm_operand_wide_string_literal) - << SL->isWide() - << SL->getSourceRange(); + << SL->isWide() << SL->getSourceRange(); return ExprError(); } - } - else if(!ForAsmLabel && getLangOpts().CPlusPlus11 && Tok.is(tok::l_paren)) { + } else if (!ForAsmLabel && getLangOpts().CPlusPlus11 && + Tok.is(tok::l_paren)) { ParenParseOption ExprType = SimpleExpr; - SourceLocation RParenLoc; + SourceLocation RParenLoc; ParsedType CastTy; - AsmString = ParseParenExpression(ExprType, true/*stopIfCastExpr*/, - false, CastTy, RParenLoc); - if(!AsmString.isUsable()) + Sema::ExpressionEvaluationContext Context = + Sema::ExpressionEvaluationContext::ConstantEvaluated; + + AsmString = ParseParenExpression(ExprType, true /*stopIfCastExpr*/, false, + CastTy, RParenLoc); + if (!AsmString.isUsable()) return ExprError(); - } - else { + } else { Diag(Tok, diag::err_asm_expected_string) - << /*and expression=*/(getLangOpts().CPlusPlus11? 0 : 1); + << /*and expression=*/(getLangOpts().CPlusPlus11 ? 0 : 1); } return Actions.ActOnGCCAsmStmtString(AsmString.get(), ForAsmLabel); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 332099bbbd437..dcd249e7a19dd 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17262,11 +17262,10 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { } template -static bool EvaluateAsStringImpl(Sema & SemaRef, - Expr *Message, - ResultType &Result, ASTContext &Ctx, - Sema::StringEvaluationContext EvalContext, - bool ErrorOnInvalidMessage) { +static bool EvaluateAsStringImpl(Sema &SemaRef, Expr *Message, + ResultType &Result, ASTContext &Ctx, + Sema::StringEvaluationContext EvalContext, + bool ErrorOnInvalidMessage) { assert(Message); assert(!Message->isTypeDependent() && !Message->isValueDependent() && @@ -17274,20 +17273,21 @@ static bool EvaluateAsStringImpl(Sema & SemaRef, if (const auto *SL = dyn_cast(Message)) { assert(SL->isUnevaluated() && "expected an unevaluated string"); - if constexpr(std::is_same_v) { - Result = APValue(APValue::UninitArray{}, SL->getLength(), SL->getLength()); - const ConstantArrayType *CAT = SemaRef.getASTContext().getAsConstantArrayType(SL->getType()); - assert(CAT && "string literal isn't an array"); - QualType CharType = CAT->getElementType(); - llvm::APSInt Value(SemaRef.getASTContext().getTypeSize(CharType), - CharType->isUnsignedIntegerType()); - for(unsigned I = 0; I < SL->getLength(); I++) { - Value = SL->getCodeUnit(I); - Result.getArrayInitializedElt(I) = APValue(Value); - } - } - else { - Result.assign(SL->getString().begin(), SL->getString().end()); + if constexpr (std::is_same_v) { + Result = + APValue(APValue::UninitArray{}, SL->getLength(), SL->getLength()); + const ConstantArrayType *CAT = + SemaRef.getASTContext().getAsConstantArrayType(SL->getType()); + assert(CAT && "string literal isn't an array"); + QualType CharType = CAT->getElementType(); + llvm::APSInt Value(SemaRef.getASTContext().getTypeSize(CharType), + CharType->isUnsignedIntegerType()); + for (unsigned I = 0; I < SL->getLength(); I++) { + Value = SL->getCodeUnit(I); + Result.getArrayInitializedElt(I) = APValue(Value); + } + } else { + Result.assign(SL->getString().begin(), SL->getString().end()); } return true; } @@ -17339,7 +17339,8 @@ static bool EvaluateAsStringImpl(Sema & SemaRef, CXXScopeSpec(), SourceLocation(), nullptr, LR, nullptr, nullptr); if (Res.isInvalid()) return ExprError(); - Res = SemaRef.BuildCallExpr(nullptr, Res.get(), Loc, {}, Loc, nullptr, false, true); + Res = SemaRef.BuildCallExpr(nullptr, Res.get(), Loc, {}, Loc, nullptr, + false, true); if (Res.isInvalid()) return ExprError(); if (Res.get()->isTypeDependent() || Res.get()->isValueDependent()) @@ -17351,27 +17352,28 @@ static bool EvaluateAsStringImpl(Sema & SemaRef, ExprResult DataE = BuildExpr(*DataMember); QualType SizeT = SemaRef.Context.getSizeType(); - QualType ConstCharPtr = - SemaRef.Context.getPointerType(SemaRef.Context.getConstType(SemaRef.Context.CharTy)); + QualType ConstCharPtr = SemaRef.Context.getPointerType( + SemaRef.Context.getConstType(SemaRef.Context.CharTy)); ExprResult EvaluatedSize = - SizeE.isInvalid() ? ExprError() - : SemaRef.BuildConvertedConstantExpression( - SizeE.get(), SizeT, Sema::CCEK_StaticAssertMessageSize); + SizeE.isInvalid() + ? ExprError() + : SemaRef.BuildConvertedConstantExpression( + SizeE.get(), SizeT, Sema::CCEK_StaticAssertMessageSize); if (EvaluatedSize.isInvalid()) { SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid_mem_fn_ret_ty) - << EvalContext << /*size*/ 0; + << EvalContext << /*size*/ 0; return false; } ExprResult EvaluatedData = DataE.isInvalid() ? ExprError() - : SemaRef.BuildConvertedConstantExpression(DataE.get(), ConstCharPtr, - Sema::CCEK_StaticAssertMessageData); + : SemaRef.BuildConvertedConstantExpression( + DataE.get(), ConstCharPtr, Sema::CCEK_StaticAssertMessageData); if (EvaluatedData.isInvalid()) { SemaRef.Diag(Loc, diag::err_user_defined_msg_invalid_mem_fn_ret_ty) - << EvalContext << /*data*/ 1; + << EvalContext << /*data*/ 1; return false; } @@ -17386,9 +17388,9 @@ static bool EvaluateAsStringImpl(Sema & SemaRef, EvaluatedData.get(), Ctx, Status) || !Notes.empty()) { SemaRef.Diag(Message->getBeginLoc(), - ErrorOnInvalidMessage ? diag::err_user_defined_msg_constexpr - : diag::warn_user_defined_msg_constexpr) - << EvalContext; + ErrorOnInvalidMessage ? diag::err_user_defined_msg_constexpr + : diag::warn_user_defined_msg_constexpr) + << EvalContext; for (const auto &Note : Notes) SemaRef.Diag(Note.first, Note.second); return !ErrorOnInvalidMessage; @@ -17399,14 +17401,15 @@ static bool EvaluateAsStringImpl(Sema & SemaRef, bool Sema::EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx, StringEvaluationContext EvalContext, bool ErrorOnInvalidMessage) { - return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext, ErrorOnInvalidMessage); - + return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext, + ErrorOnInvalidMessage); } bool Sema::EvaluateAsString(Expr *Message, std::string &Result, ASTContext &Ctx, StringEvaluationContext EvalContext, bool ErrorOnInvalidMessage) { - return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext, ErrorOnInvalidMessage); + return EvaluateAsStringImpl(*this, Message, Result, Ctx, EvalContext, + ErrorOnInvalidMessage); } Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, @@ -17455,7 +17458,7 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, if (!Failed && AssertMessage && Cond.getBoolValue()) { std::string Str; EvaluateAsString(AssertMessage, Str, Context, - StringEvaluationContext::StaticAssert, + StringEvaluationContext::StaticAssert, /*ErrorOnInvalidMessage=*/false); } diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp index e168f57f8f303..4507a21a4c111 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -205,14 +205,14 @@ static StringRef extractRegisterName(const Expr *Expression, // conflicted clobber, else returns nullptr static SourceLocation getClobberConflictLocation(MultiExprArg Exprs, Expr **Constraints, - Expr **Clobbers, int NumClobbers, - unsigned NumLabels, + Expr **Clobbers, int NumClobbers, unsigned NumLabels, const TargetInfo &Target, ASTContext &Cont) { llvm::StringSet<> InOutVars; // Collect all the input and output registers from the extended asm // statement in order to check for conflicts with the clobber list for (unsigned int i = 0; i < Exprs.size() - NumLabels; ++i) { - std::string Constraint = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraints[i]); + std::string Constraint = + GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraints[i]); StringRef InOutReg = Target.getConstraintRegister( Constraint, extractRegisterName(Exprs[i], Target)); if (InOutReg != "") @@ -221,7 +221,8 @@ getClobberConflictLocation(MultiExprArg Exprs, Expr **Constraints, // Check for each item in the clobber list if it conflicts with the input // or output for (int i = 0; i < NumClobbers; ++i) { - std::string Clobber = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Clobbers[i]); + std::string Clobber = + GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Clobbers[i]); // We only check registers, therefore we don't check cc and memory // clobbers if (Clobber == "cc" || Clobber == "memory" || Clobber == "unwind") @@ -234,24 +235,24 @@ getClobberConflictLocation(MultiExprArg Exprs, Expr **Constraints, return SourceLocation(); } -ExprResult Sema::ActOnGCCAsmStmtString(Expr* Expr, bool ForAsmLabel) { - if(!Expr) +ExprResult Sema::ActOnGCCAsmStmtString(Expr *Expr, bool ForAsmLabel) { + if (!Expr) return ExprError(); - if(auto* SL = dyn_cast(Expr)) { + if (auto *SL = dyn_cast(Expr)) { assert(SL->isOrdinary()); if (ForAsmLabel && SL->getString().empty()) { Diag(Expr->getBeginLoc(), diag::err_asm_operand_empty_string) - << SL->getSourceRange(); + << SL->getSourceRange(); } return SL; } - if(DiagnoseUnexpandedParameterPack(Expr)) + if (DiagnoseUnexpandedParameterPack(Expr)) return ExprError(); - if(Expr->getDependence() != ExprDependence::None) + if (Expr->getDependence() != ExprDependence::None) return Expr; APValue V; - if(!EvaluateAsString(Expr, V, getASTContext(), StringEvaluationContext::Asm, + if (!EvaluateAsString(Expr, V, getASTContext(), StringEvaluationContext::Asm, /*ErrorOnInvalid=*/true)) return ExprError(); @@ -259,7 +260,8 @@ ExprResult Sema::ActOnGCCAsmStmtString(Expr* Expr, bool ForAsmLabel) { Diag(Expr->getBeginLoc(), diag::err_asm_operand_empty_string); } - ConstantExpr* Res = ConstantExpr::Create(getASTContext(), Expr, ConstantResultStorageKind::APValue); + ConstantExpr *Res = ConstantExpr::Create(getASTContext(), Expr, + ConstantResultStorageKind::APValue); Res->SetResult(V, getASTContext()); return Res; } @@ -281,27 +283,28 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, auto CreateGCCAsmStmt = [&] { return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, constraints.data(), Exprs.data(), asmString, - NumClobbers, clobbers.data(), NumLabels, RParenLoc); + GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, NumInputs, + Names, constraints.data(), Exprs.data(), asmString, + NumClobbers, clobbers.data(), NumLabels, RParenLoc); }; - if(asmString->getDependence() != ExprDependence::None - || llvm::any_of(constraints, [] (Expr* E) { + if (asmString->getDependence() != ExprDependence::None || + llvm::any_of( + constraints, + [](Expr *E) { return E->getDependence() != ExprDependence::None; }) || + llvm::any_of(clobbers, [](Expr *E) { return E->getDependence() != ExprDependence::None; - }) - || llvm::any_of(clobbers, [] (Expr* E) { - return E->getDependence() != ExprDependence::None; - })) - return CreateGCCAsmStmt(); + })) + return CreateGCCAsmStmt(); for (unsigned i = 0; i != NumOutputs; i++) { - Expr* Constraint = constraints[i]; + Expr *Constraint = constraints[i]; StringRef OutputName; if (Names[i]) OutputName = Names[i]->getName(); - std::string ConstraintStr = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint); + std::string ConstraintStr = + GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint); TargetInfo::ConstraintInfo Info(ConstraintStr, OutputName); if (!Context.getTargetInfo().validateOutputConstraint(Info) && @@ -372,7 +375,9 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned Size = Context.getTypeSize(OutputExpr->getType()); if (!Context.getTargetInfo().validateOutputSize( - FeatureMap, GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint), Size)) { + FeatureMap, + GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint), + Size)) { targetDiag(OutputExpr->getBeginLoc(), diag::err_asm_invalid_output_size) << Info.getConstraintStr(); return CreateGCCAsmStmt(); @@ -388,12 +393,14 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, if (Names[i]) InputName = Names[i]->getName(); - std::string ConstraintStr = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint); + std::string ConstraintStr = + GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint); TargetInfo::ConstraintInfo Info(ConstraintStr, InputName); if (!Context.getTargetInfo().validateInputConstraint(OutputConstraintInfos, Info)) { - targetDiag(Constraint->getBeginLoc(), diag::err_asm_invalid_input_constraint) + targetDiag(Constraint->getBeginLoc(), + diag::err_asm_invalid_input_constraint) << Info.getConstraintStr(); return CreateGCCAsmStmt(); } @@ -480,8 +487,8 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, return StmtError(); unsigned Size = Context.getTypeSize(Ty); - if (!Context.getTargetInfo().validateInputSize(FeatureMap, - ConstraintStr, Size)) + if (!Context.getTargetInfo().validateInputSize(FeatureMap, ConstraintStr, + Size)) return targetDiag(InputExpr->getBeginLoc(), diag::err_asm_invalid_input_size) << Info.getConstraintStr(); @@ -493,15 +500,17 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, for (unsigned i = 0; i != NumClobbers; i++) { Expr *ClobberExpr = clobbers[i]; - std::string Clobber = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(ClobberExpr); + std::string Clobber = + GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(ClobberExpr); if (!Context.getTargetInfo().isValidClobber(Clobber)) { - targetDiag(ClobberExpr->getBeginLoc(), diag::err_asm_unknown_register_name) + targetDiag(ClobberExpr->getBeginLoc(), + diag::err_asm_unknown_register_name) << Clobber; - return new (Context) - GCCAsmStmt(Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, - NumInputs, Names, constraints.data(), Exprs.data(), asmString, - NumClobbers, clobbers.data(), NumLabels, RParenLoc); + return new (Context) GCCAsmStmt( + Context, AsmLoc, IsSimple, IsVolatile, NumOutputs, NumInputs, Names, + constraints.data(), Exprs.data(), asmString, NumClobbers, + clobbers.data(), NumLabels, RParenLoc); } if (Clobber == "unwind") { @@ -519,8 +528,8 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, // Validate the asm string, ensuring it makes sense given the operands we // have. - auto GetLocation = [this](const Expr* Str, unsigned Offset) { - if(auto* SL = dyn_cast(Str)) + auto GetLocation = [this](const Expr *Str, unsigned Offset) { + if (auto *SL = dyn_cast(Str)) return getLocationOfStringLiteralByte(SL, Offset); return Str->getBeginLoc(); }; @@ -568,8 +577,8 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, unsigned Size = Context.getTypeSize(Ty); std::string SuggestedModifier; if (!Context.getTargetInfo().validateConstraintModifier( - GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint), Piece.getModifier(), Size, - SuggestedModifier)) { + GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(Constraint), + Piece.getModifier(), Size, SuggestedModifier)) { targetDiag(Exprs[ConstraintIdx]->getBeginLoc(), diag::warn_asm_mismatched_size_modifier); @@ -577,9 +586,10 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, auto B = targetDiag(Piece.getRange().getBegin(), diag::note_asm_missing_constraint_modifier) << SuggestedModifier; - if(isa(Constraint)) { + if (isa(Constraint)) { SuggestedModifier = "%" + SuggestedModifier + Piece.getString(); - B << FixItHint::CreateReplacement(Piece.getRange(), SuggestedModifier); + B << FixItHint::CreateReplacement(Piece.getRange(), + SuggestedModifier); } } } @@ -740,10 +750,9 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, } // Check for conflicts between clobber list and input or output lists - SourceLocation ConstraintLoc = - getClobberConflictLocation(Exprs, constraints.data(), clobbers.data(), NumClobbers, - NumLabels, - Context.getTargetInfo(), Context); + SourceLocation ConstraintLoc = getClobberConflictLocation( + Exprs, constraints.data(), clobbers.data(), NumClobbers, NumLabels, + Context.getTargetInfo(), Context); if (ConstraintLoc.isValid()) targetDiag(ConstraintLoc, diag::error_inoutput_conflict_with_clobber); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 06d41a8154ca3..8cf82a4c33782 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -8552,7 +8552,6 @@ template StmtResult TreeTransform::TransformGCCAsmStmt(GCCAsmStmt *S) { - SmallVector Constraints; SmallVector Exprs; SmallVector Names; @@ -8561,15 +8560,15 @@ TreeTransform::TransformGCCAsmStmt(GCCAsmStmt *S) { bool ExprsChanged = false; - auto RebuildString = [&] (Expr* E) { - ExprResult Result = getDerived().TransformExpr(E); - if (Result.isInvalid()) - return Result; - if(!Result.isInvalid() && Result.get() != E) { - ExprsChanged = true; - Result = SemaRef.ActOnGCCAsmStmtString(Result.get(), /*ForLabel=*/false); - } + auto RebuildString = [&](Expr *E) { + ExprResult Result = getDerived().TransformExpr(E); + if (Result.isInvalid()) return Result; + if (!Result.isInvalid() && Result.get() != E) { + ExprsChanged = true; + Result = SemaRef.ActOnGCCAsmStmtString(Result.get(), /*ForLabel=*/false); + } + return Result; }; // Go through the outputs. @@ -8627,10 +8626,10 @@ TreeTransform::TransformGCCAsmStmt(GCCAsmStmt *S) { // Go through the clobbers. for (unsigned I = 0, E = S->getNumClobbers(); I != E; ++I) { - ExprResult Result = RebuildString(S->getClobberExpr(I)); - if (Result.isInvalid()) - return StmtError(); - Clobbers.push_back(Result.get()); + ExprResult Result = RebuildString(S->getClobberExpr(I)); + if (Result.isInvalid()) + return StmtError(); + Clobbers.push_back(Result.get()); } ExprResult AsmString = RebuildString(S->getAsmStringExpr()); diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 7ad1216cb53e2..2db7d9346c463 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -390,7 +390,7 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { // Outputs and inputs SmallVector Names; - SmallVector Constraints; + SmallVector Constraints; SmallVector Exprs; for (unsigned I = 0, N = NumOutputs + NumInputs; I != N; ++I) { Names.push_back(Record.readIdentifier()); @@ -399,7 +399,7 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { } // Constraints - SmallVector Clobbers; + SmallVector Clobbers; for (unsigned I = 0; I != NumClobbers; ++I) Clobbers.push_back(cast_or_null(Record.readSubStmt())); From 8c67e65ac02929b45f31183f49dfe6f0ae70d1ce Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 12:46:26 +0100 Subject: [PATCH 03/16] Add parsing tests --- clang/lib/Parse/ParseStmtAsm.cpp | 4 ++-- clang/lib/Parse/Parser.cpp | 14 ++++++----- clang/test/Parser/asm.cpp | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp index 94144225ff406..e32094bdd99b8 100644 --- a/clang/lib/Parse/ParseStmtAsm.cpp +++ b/clang/lib/Parse/ParseStmtAsm.cpp @@ -884,8 +884,8 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { bool Parser::ParseAsmOperandsOpt(SmallVectorImpl &Names, SmallVectorImpl &Constraints, SmallVectorImpl &Exprs) { - // 'asm-operands' isn't present? - if (!isTokenStringLiteral() && Tok.isNot(tok::l_square)) + // 'asm-operands' isn't present + if (Tok.isOneOf(tok::colon, tok::coloncolon, tok::r_paren)) return false; while (true) { diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 66d2e3238317e..fc2383b563172 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1687,16 +1687,18 @@ ExprResult Parser::ParseAsmStringLiteral(bool ForAsmLabel) { SourceLocation RParenLoc; ParsedType CastTy; - Sema::ExpressionEvaluationContext Context = - Sema::ExpressionEvaluationContext::ConstantEvaluated; - + EnterExpressionEvaluationContext ConstantEvaluated( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); AsmString = ParseParenExpression(ExprType, true /*stopIfCastExpr*/, false, CastTy, RParenLoc); - if (!AsmString.isUsable()) + if (!AsmString.isInvalid()) + AsmString = Actions.ActOnConstantExpression(AsmString); + + if (AsmString.isInvalid()) return ExprError(); } else { - Diag(Tok, diag::err_asm_expected_string) - << /*and expression=*/(getLangOpts().CPlusPlus11 ? 0 : 1); + Diag(Tok, diag::err_asm_expected_string) << /*and expression=*/( + (getLangOpts().CPlusPlus11 && !ForAsmLabel) ? 0 : 1); } return Actions.ActOnGCCAsmStmtString(AsmString.get(), ForAsmLabel); diff --git a/clang/test/Parser/asm.cpp b/clang/test/Parser/asm.cpp index de156877ec470..5bbc385e743d7 100644 --- a/clang/test/Parser/asm.cpp +++ b/clang/test/Parser/asm.cpp @@ -12,3 +12,44 @@ void f() { [[]] asm(""); [[gnu::deprecated]] asm(""); // expected-warning {{'deprecated' attribute ignored}} } + + +struct string_view { + int S; + const char* D; + constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {} + constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {} + constexpr int size() const { + return S; + } + constexpr const char* data() const { + return D; + } +}; + +// Neither gcc nor clang support expressions in label +int foo1 asm ((string_view("test"))); // expected-error {{expected string literal in 'asm'}} +int func() asm ((string_view("test"))); // expected-error {{expected string literal in 'asm'}} + + +void f2() { + asm(string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}} + asm("" : string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}} + asm("" : : string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}} + asm("" : : : string_view("")); // expected-error {{expected ')'}} + asm("" :: string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}} + asm(::string_view("")); // expected-error {{expected string literal or parenthesized constant expression in 'asm'}} + + int i; + + asm((string_view(""))); + asm((::string_view(""))); + asm("" : (::string_view("+g")) (i)); + asm("" : (::string_view("+g"))); // expected-error {{expected '(' after 'asm operand'}} + asm("" : (::string_view("+g")) (i) : (::string_view("g")) (0)); + asm("" : (::string_view("+g")) (i) : (::string_view("g"))); // expected-error {{expected '(' after 'asm operand'}} + asm("" : (::string_view("+g")) (i) : (::string_view("g")) (0) : (string_view("memory"))); + + + asm((0)); // expected-error {{the expression in this asm operand must be a string literal or an object with 'data()' and 'size()' member functions}} +} From 1d295069ffdf514e0d51335df37672040c7395cb Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 14:38:40 +0100 Subject: [PATCH 04/16] add missing header --- clang/lib/Parse/Parser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index fc2383b563172..83dd7b17c73b8 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -19,6 +19,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/SemaCodeCompletion.h" From 6e507819b3e3cc187cb869e7ba2c67cc3b250121 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 14:38:57 +0100 Subject: [PATCH 05/16] Sema Tests --- clang/test/Sema/gnu-asm-constexpr.cpp | 127 ++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 clang/test/Sema/gnu-asm-constexpr.cpp diff --git a/clang/test/Sema/gnu-asm-constexpr.cpp b/clang/test/Sema/gnu-asm-constexpr.cpp new file mode 100644 index 0000000000000..8813b873fab0c --- /dev/null +++ b/clang/test/Sema/gnu-asm-constexpr.cpp @@ -0,0 +1,127 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -triple x86_64-gnu-linux + +template +struct RAIIBase { + constexpr RAIIBase(const char* in) { + s = __builtin_strlen(in); + d = new char[s + 1]; // expected-note 4{{allocation performed here was not deallocated}} + for(int i = 0 ; i < s; i++) + d[i] = in[i]; + } + int s; + char* d; + constexpr unsigned long size() const { + return s; + } + constexpr const char* data() const { + return d; + } + constexpr ~RAIIBase() { + if constexpr(!Leak) + delete[] d; + } +}; + +using RAII = RAIIBase; +using RAIILeak = RAIIBase; + +void test_leaks(int i) { + asm((RAII("nop")) : (RAII("+ir")) (i) : (RAII("g")) (i) : (RAII("memory"))); + asm((RAIILeak("nop"))); // expected-error {{the expression in this asm operand must be produced by a constant expression}} + asm((RAII("nop")) + : (RAIILeak("+ir")) (i) // expected-error {{the expression in this asm operand must be produced by a constant expression}} + :: + ); + asm((RAII("nop")) + : (RAII("+ir")) (i) + : (RAIILeak("g")) (i) // expected-error {{the expression in this asm operand must be produced by a constant expression}} + : + ); + asm((RAII("nop")) + : (RAII("+ir")) (i) + : (RAII("g")) (i) + : (RAIILeak("memory")) // expected-error {{the expression in this asm operand must be produced by a constant expression}} + ); +} + +struct NotAString{}; +struct MessageInvalidSize { + constexpr unsigned long size(int) const; // expected-note {{'size' declared here}} + constexpr const char* data() const; +}; +struct MessageInvalidData { + constexpr unsigned long size() const; + constexpr const char* data(int) const; // expected-note {{'data' declared here}} +}; + + +struct WMessage { + constexpr unsigned long long size() const {return 0;}; + constexpr const wchar_t* data() const {return L"";} +}; + +struct string_view { + int S; + const char* D; + constexpr string_view() : S(0), D(0){} + constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {} + constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {} + constexpr int size() const { + return S; + } + constexpr const char* data() const { + return D; + } +}; + + +void f() { + asm(("")); // expected-error {{the expression in this asm operand must be a string literal or an object with 'data()' and 'size()' member functions}} + asm((NotAString{})); // expected-error {{the string object in this asm operand is missing 'data()' and 'size()' member functions}} + asm((MessageInvalidData{})); // expected-error {{the expression in this asm operand must have a 'data()' member function returning an object convertible to 'const char *'}} \ + // expected-error {{too few arguments to function call, expected 1, have 0}} + asm((MessageInvalidSize{})); // expected-error {{the expression in this asm operand must have a 'size()' member function returning an object convertible to 'std::size_t'}} \ + // expected-error {{too few arguments to function call, expected 1, have 0}} + + asm((WMessage{})); // expected-error {{value of type 'const wchar_t *' is not implicitly convertible to 'const char *'}} \ + // expected-error {{the expression in this asm operand must have a 'data()' member function returning an object convertible to 'const char *'}} +} + +template +void test_packs() { + asm((U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}} + asm("" : (U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}} + asm("" :: (U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}} + asm("" ::: (U{})); // expected-error {{expression contains unexpanded parameter pack 'U'}} +} + +template +void test_dependent1(int i) { + asm((T{})); // #err-int + asm("" : (T{"+g"})(i)); // #err-int2 + asm("" :: (T{"g"})(i)); // #err-int3 + asm("" ::: (T{"memory"})); // #err-int4 +} + +template void test_dependent1(int); +// expected-note@-1 {{in instantiation of function template specialization}} +// expected-error@#err-int {{the expression in this asm operand must be a string literal or an object with 'data()' and 'size()' member functions}} +// expected-error@#err-int2 {{cannot initialize a value of type 'int' with an lvalue of type 'const char[3]'}} +// expected-error@#err-int3 {{cannot initialize a value of type 'int' with an lvalue of type 'const char[2]'}} +// expected-error@#err-int4 {{cannot initialize a value of type 'int' with an lvalue of type 'const char[7]'}} + +template void test_dependent1(int); + + +template +void test_dependent2(int i) { + asm("" : (T{"g"})(i)); // #err-invalid1 + asm("" :: (T{"+g"})(i)); // #err-invalid2 + asm("" ::: (T{"foo"})); // #err-invalid3 +} +template void test_dependent2(int); +// expected-note@-1 {{in instantiation of function template specialization}} +// expected-error@#err-invalid1 {{invalid output constraint 'g' in asm}} +// expected-error@#err-invalid2 {{invalid input constraint '+g' in asm}} +// expected-error@#err-invalid3 {{unknown register name 'foo' in asm}} + From 363f0b76bd154bfd902ff9f624d5ff37b540e46c Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 16:04:29 +0100 Subject: [PATCH 06/16] Add docs and has_extension support --- clang/docs/LanguageExtensions.rst | 18 ++++++++++++++++++ clang/docs/ReleaseNotes.rst | 9 +++++++++ clang/include/clang/Basic/Features.def | 1 + clang/test/Parser/asm.cpp | 4 ++++ 4 files changed, 32 insertions(+) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index f8d1bc9fe2624..a990eefc8eeac 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1958,6 +1958,24 @@ references can be used instead of numeric references. return -1; } + +Constexpr strings in GNU ASM statememts +======================================= + +In C++11 mode (and greater), Clang supports specifying the template, +constraints, and clobber strings with a parenthesized constant expression +producing an object with ``data`` and ``size`` member functions, +such as ``std::string``. + +Query for this feature with ``__has_extension(gnu_asm_constexpr_strings)``. + +.. code-block:: c++ + + int foo() { + asm((std::string_view("nop")) ::: (std::string_view("memory"))); + } + + Objective-C Features ==================== diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 48da5558b3f38..c438b9a997ad8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -74,6 +74,15 @@ What's New in Clang |release|? C++ Language Changes -------------------- +- Similarly to GCC, Clang now supports constant expressions in + the strings of a GNU ``asm`` statement. + + .. code-block:: c++ + + int foo() { + asm((std::string_view("nop")) ::: (std::string_view("memory"))); + } + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 92b1705d15227..05ce214935fad 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -306,6 +306,7 @@ EXTENSION(statement_attributes_with_gnu_syntax, true) EXTENSION(gnu_asm, LangOpts.GNUAsm) EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm) EXTENSION(gnu_asm_goto_with_outputs_full, LangOpts.GNUAsm) +EXTENSION(gnu_asm_constexpr_strings, LangOpts.GNUAsm && LangOpts.CPlusPlus11) EXTENSION(matrix_types, LangOpts.MatrixTypes) EXTENSION(matrix_types_scalar_division, true) EXTENSION(cxx_attributes_on_using_declarations, LangOpts.CPlusPlus11) diff --git a/clang/test/Parser/asm.cpp b/clang/test/Parser/asm.cpp index 5bbc385e743d7..4d590992f6b09 100644 --- a/clang/test/Parser/asm.cpp +++ b/clang/test/Parser/asm.cpp @@ -14,6 +14,10 @@ void f() { } +#if !__has_extension(gnu_asm_constexpr_strings) +#error Extension 'gnu_asm_constexpr_strings' should be available by default +#endif + struct string_view { int S; const char* D; From 8f15264e37cdb772138e5b0c13045538ce30901b Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 17:01:53 +0100 Subject: [PATCH 07/16] Address Erich's feedback --- clang/lib/AST/ExprConstant.cpp | 17 ++++++++--------- clang/lib/AST/StmtPrinter.cpp | 21 ++++----------------- clang/lib/Sema/TreeTransform.h | 4 ++-- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 70f3531524620..7a0acbc3c0af5 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -17945,11 +17945,13 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, uint64_t Size = SizeValue.getZExtValue(); + // FIXME: better protect against invalid or excessive sizes if constexpr (std::is_same_v) Result = APValue(APValue::UninitArray{}, Size, Size); - // else - // Result.reserve(Size); - + else { + if (Size < Result.max_size()) + Result.reserve(Size); + } if (!::EvaluatePointer(PtrExpression, String, Info)) return false; @@ -17964,19 +17966,16 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, Result.getArrayInitializedElt(I) = std::move(Char); } else { APSInt C = Char.getInt(); + assert(C.getBitWidth() <= 8 && + "string element not representable in char"); Result.push_back(static_cast(C.getExtValue())); } if (!HandleLValueArrayAdjustment(Info, PtrExpression, String, CharTy, 1)) return false; } - if (!Scope.destroy()) - return false; - if (!CheckMemoryLeaks(Info)) - return false; - - return true; + return Scope.destroy() && CheckMemoryLeaks(Info); } bool Expr::EvaluateCharRangeAsString(std::string &Result, diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index bceab177a6aa3..fe9784a15b76b 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -497,19 +497,6 @@ void StmtPrinter::VisitReturnStmt(ReturnStmt *Node) { if (Policy.IncludeNewlines) OS << NL; } -static void PrintGCCAsmString(raw_ostream &OS, Expr *E) { - if (E->getDependence()) { - OS << "<>"; - return; - } - - std::string Str = GCCAsmStmt::ExtractStringFromGCCAsmStmtComponent(E); - llvm::SmallVector Out; - // FIXME: Unify with StringLiteral::outputString - EscapeStringForDiagnostic(Str, Out); - OS << '"' << Out << '"'; -} - void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { Indent() << "asm "; @@ -520,7 +507,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { OS << "goto "; OS << "("; - PrintGCCAsmString(OS, Node->getAsmStringExpr()); + Visit(Node->getAsmStringExpr()); // Outputs if (Node->getNumOutputs() != 0 || Node->getNumInputs() != 0 || @@ -537,7 +524,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { OS << "] "; } - PrintGCCAsmString(OS, Node->getOutputConstraintExpr(i)); + Visit(Node->getOutputConstraintExpr(i)); OS << " ("; Visit(Node->getOutputExpr(i)); OS << ")"; @@ -558,7 +545,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { OS << "] "; } - PrintGCCAsmString(OS, Node->getInputConstraintExpr(i)); + Visit(Node->getInputConstraintExpr(i)); OS << " ("; Visit(Node->getInputExpr(i)); OS << ")"; @@ -572,7 +559,7 @@ void StmtPrinter::VisitGCCAsmStmt(GCCAsmStmt *Node) { if (i != 0) OS << ", "; - PrintGCCAsmString(OS, Node->getClobberExpr(i)); + Visit(Node->getClobberExpr(i)); } // Labels diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 8cf82a4c33782..b5de98e3989ea 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -8562,9 +8562,9 @@ TreeTransform::TransformGCCAsmStmt(GCCAsmStmt *S) { auto RebuildString = [&](Expr *E) { ExprResult Result = getDerived().TransformExpr(E); - if (Result.isInvalid()) + if (!Result.isUsable()) return Result; - if (!Result.isInvalid() && Result.get() != E) { + if (Result.get() != E) { ExprsChanged = true; Result = SemaRef.ActOnGCCAsmStmtString(Result.get(), /*ForLabel=*/false); } From 4ea32fce9e739d3a658529319cd45337a5d8d51d Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 17:11:59 +0100 Subject: [PATCH 08/16] format --- clang/lib/AST/ExprConstant.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 7a0acbc3c0af5..7803b1026aab9 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -17966,8 +17966,10 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, Result.getArrayInitializedElt(I) = std::move(Char); } else { APSInt C = Char.getInt(); + assert(C.getBitWidth() <= 8 && "string element not representable in char"); + Result.push_back(static_cast(C.getExtValue())); } From 599affb722221681c92c26d8a20cedaf115ee304 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 18:08:02 +0100 Subject: [PATCH 09/16] Codegen tests --- clang/test/CodeGenCXX/gnu-asm-constexpr.cpp | 38 +++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 clang/test/CodeGenCXX/gnu-asm-constexpr.cpp diff --git a/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp b/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp new file mode 100644 index 0000000000000..e00db74a9edac --- /dev/null +++ b/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s + +struct string_view { + int S; + const char* D; + constexpr string_view() : S(0), D(0){} + constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {} + constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {} + constexpr int size() const { + return S; + } + constexpr const char* data() const { + return D; + } +}; + +int func() {return 0;}; + +void f() { + + asm((string_view("")) ::(string_view("r"))(func())); + // CHECK: %[[CALL:.*]] = call noundef i32 @_Z4funcv + // CHECK: call void asm sideeffect "", "r,~{dirflag},~{fpsr},~{flags}" + asm("" :::(string_view("memory"))); + // CHECK: call void asm sideeffect "", "~{memory},~{dirflag},~{fpsr},~{flags}" +} + +void foo(unsigned long long addr, unsigned long long a0) { + register unsigned long long result asm("rax"); + register unsigned long long b0 asm("rdi"); + + b0 = a0; + + asm((string_view("call *%1")) : (string_view("=r")) (result) + : (string_view("r"))(addr), (string_view("r")) (b0) : (string_view("memory"))); + + // CHECK:{{.*}} call i64 asm "call *$1", "={rax},r,{rdi},~{memory},~{dirflag},~{fpsr},~{flags}" +} From 1ccea7fad67651a784ada7ae95a6b2dce506f5a6 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 18:21:41 +0100 Subject: [PATCH 10/16] cleanup non sense in EvaluateAsStringImpl --- clang/lib/Sema/SemaDeclCXX.cpp | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index dcd249e7a19dd..c3fbf430030c4 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17300,12 +17300,10 @@ static bool EvaluateAsStringImpl(Sema &SemaRef, Expr *Message, return false; } - auto FindMember = [&](StringRef Member, bool &Empty, - bool Diag = false) -> std::optional { + auto FindMember = [&](StringRef Member) -> std::optional { DeclarationName DN = SemaRef.PP.getIdentifierInfo(Member); LookupResult MemberLookup(SemaRef, DN, Loc, Sema::LookupMemberName); SemaRef.LookupQualifiedName(MemberLookup, RD); - Empty = MemberLookup.empty(); OverloadCandidateSet Candidates(MemberLookup.getNameLoc(), OverloadCandidateSet::CSK_Normal); if (MemberLookup.empty()) @@ -17313,23 +17311,14 @@ static bool EvaluateAsStringImpl(Sema &SemaRef, Expr *Message, return std::move(MemberLookup); }; - bool SizeNotFound, DataNotFound; - std::optional SizeMember = FindMember("size", SizeNotFound); - std::optional DataMember = FindMember("data", DataNotFound); - if (SizeNotFound || DataNotFound) { + std::optional SizeMember = FindMember("size"); + std::optional DataMember = FindMember("data"); + if (!SizeMember || !DataMember) { SemaRef.Diag(Loc, diag::err_user_defined_msg_missing_member_function) << EvalContext - << ((SizeNotFound && DataNotFound) ? 2 - : SizeNotFound ? 0 - : 1); - return false; - } - - if (!SizeMember || !DataMember) { - if (!SizeMember) - FindMember("size", SizeNotFound, /*Diag=*/true); - if (!DataMember) - FindMember("data", DataNotFound, /*Diag=*/true); + << ((!SizeMember && !DataMember) ? 2 + : !SizeMember ? 0 + : 1); return false; } From 98e0d6c5fe835f8845f7357c2917d5101108118c Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 18:26:58 +0100 Subject: [PATCH 11/16] move test to correct folder --- clang/test/{Sema => SemaCXX}/gnu-asm-constexpr.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clang/test/{Sema => SemaCXX}/gnu-asm-constexpr.cpp (100%) diff --git a/clang/test/Sema/gnu-asm-constexpr.cpp b/clang/test/SemaCXX/gnu-asm-constexpr.cpp similarity index 100% rename from clang/test/Sema/gnu-asm-constexpr.cpp rename to clang/test/SemaCXX/gnu-asm-constexpr.cpp From a1f727e4236f45443553a1a5334ad0bfaf4f9e10 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 23:21:24 +0100 Subject: [PATCH 12/16] Add !srcloc for non string literal expressions --- clang/lib/CodeGen/CGStmt.cpp | 12 +++++++----- clang/test/CodeGenCXX/gnu-asm-constexpr.cpp | 13 +++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index c243d5c98239b..9860f23dc9e28 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -2586,12 +2586,14 @@ static void UpdateAsmCallInst(llvm::CallBase &Result, bool HasSideEffect, // Slap the source location of the inline asm into a !srcloc metadata on the // call. - if (const auto *gccAsmStmt = dyn_cast(&S)) { - if (const StringLiteral *SL = - dyn_cast(gccAsmStmt->getAsmStringExpr())) - Result.setMetadata("srcloc", getAsmSrcLocInfo(SL, CGF)); + const StringLiteral *SL; + if (const auto *gccAsmStmt = dyn_cast(&S); + gccAsmStmt && + (SL = dyn_cast(gccAsmStmt->getAsmStringExpr()))) { + Result.setMetadata("srcloc", getAsmSrcLocInfo(SL, CGF)); } else { - // At least put the line number on MS inline asm blobs. + // At least put the line number on MS inline asm blobs and GCC asm constexpr + // strings. llvm::Constant *Loc = llvm::ConstantInt::get(CGF.Int64Ty, S.getAsmLoc().getRawEncoding()); Result.setMetadata("srcloc", diff --git a/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp b/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp index e00db74a9edac..65988f41f1710 100644 --- a/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp +++ b/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp @@ -1,3 +1,4 @@ +// RUN: %clang_cc1 -triple x86_64 %s -S -o /dev/null -Werror -verify // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s struct string_view { @@ -36,3 +37,15 @@ void foo(unsigned long long addr, unsigned long long a0) { // CHECK:{{.*}} call i64 asm "call *$1", "={rax},r,{rdi},~{memory},~{dirflag},~{fpsr},~{flags}" } + + +void test_srcloc() { + asm((string_view( // expected-error {{invalid instruction mnemonic 'nonsense'}} \ + // expected-error {{invalid instruction mnemonic 'foobar'}} \ + // expected-note@1 {{instantiated into assembly here}} \ + // expected-note@2 {{instantiated into assembly here}} + R"o(nonsense + foobar)o") + + ) ::(string_view("r"))(func())); + } From cad2cec29e844e2ac8cc9905fbdab68e9703e01c Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 13 Mar 2025 23:25:09 +0100 Subject: [PATCH 13/16] require target --- clang/test/CodeGenCXX/gnu-asm-constexpr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp b/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp index 65988f41f1710..d9c633ea69283 100644 --- a/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp +++ b/clang/test/CodeGenCXX/gnu-asm-constexpr.cpp @@ -1,3 +1,4 @@ +// REQUIRES: x86-registered-target // RUN: %clang_cc1 -triple x86_64 %s -S -o /dev/null -Werror -verify // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s From b9ffe4c45f69b931faaac06c24c3a16f4a70cf37 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Mon, 17 Mar 2025 17:31:25 +0100 Subject: [PATCH 14/16] Address Erich's feedback --- clang/docs/LanguageExtensions.rst | 9 +++++++-- clang/include/clang/AST/Stmt.h | 2 +- clang/lib/Serialization/ASTReaderStmt.cpp | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index a990eefc8eeac..a8718025341f5 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1964,8 +1964,13 @@ Constexpr strings in GNU ASM statememts In C++11 mode (and greater), Clang supports specifying the template, constraints, and clobber strings with a parenthesized constant expression -producing an object with ``data`` and ``size`` member functions, -such as ``std::string``. +producing an object with the following member functions + +.. code-block:: c++ + constexpr const char* data() const; + constexpr size_t size() const; + +such as ``std::string``, ``std::string_view``, ``std::vector``. Query for this feature with ``__has_extension(gnu_asm_constexpr_strings)``. diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 88ae3aeefa869..e779f94d16b94 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -3327,7 +3327,7 @@ class GCCAsmStmt : public AsmStmt { const Expr *getAsmStringExpr() const { return AsmStr; } Expr *getAsmStringExpr() { return AsmStr; } - void setAsmString(Expr *E) { AsmStr = E; } + void setAsmStringExpr(Expr *E) { AsmStr = E; } std::string getAsmString() const; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 2db7d9346c463..1e13bcfe60b47 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -381,7 +381,7 @@ void ASTStmtReader::VisitGCCAsmStmt(GCCAsmStmt *S) { VisitAsmStmt(S); S->NumLabels = Record.readInt(); S->setRParenLoc(readSourceLocation()); - S->setAsmString(cast_or_null(Record.readSubStmt())); + S->setAsmStringExpr(cast_or_null(Record.readSubStmt())); unsigned NumOutputs = S->getNumOutputs(); unsigned NumInputs = S->getNumInputs(); From d17ca80e7f5deb8ad1bfd788a68f37ca57f4f332 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Mon, 17 Mar 2025 17:47:15 +0100 Subject: [PATCH 15/16] fix docs --- clang/docs/LanguageExtensions.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index a8718025341f5..994c96f12d44b 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1967,6 +1967,7 @@ constraints, and clobber strings with a parenthesized constant expression producing an object with the following member functions .. code-block:: c++ + constexpr const char* data() const; constexpr size_t size() const; From 328f93c43d90f269d27c32d9ca8e98320ef818bb Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Mon, 17 Mar 2025 19:21:15 +0100 Subject: [PATCH 16/16] More docs --- clang/docs/LanguageExtensions.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 994c96f12d44b..2af67df98a8ed 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1972,6 +1972,8 @@ producing an object with the following member functions constexpr size_t size() const; such as ``std::string``, ``std::string_view``, ``std::vector``. +This mechanism follow the same rules as ``static_assert`` messages in +C++26, see ``[dcl.pre]/p12``. Query for this feature with ``__has_extension(gnu_asm_constexpr_strings)``.