From d61487490022aaacc34249475fac3e208c7d767e Mon Sep 17 00:00:00 2001 From: James Y Knight Date: Mon, 28 Mar 2022 18:27:18 -0400 Subject: [PATCH] [Clang] Implement __builtin_source_location. This builtin returns the address of a global instance of the `std::source_location::__impl` type, which must be defined (with an appropriate shape) before calling the builtin. It will be used to implement std::source_location in libc++ in a future change. The builtin is compatible with GCC's implementation, and libstdc++'s usage. An intentional divergence is that GCC declares the builtin's return type to be `const void*` (for ease-of-implementation reasons), while Clang uses the actual type, `const std::source_location::__impl*`. In order to support this new functionality, I've also added a new 'UnnamedGlobalConstantDecl'. This artificial Decl is modeled after MSGuidDecl, and is used to represent a generic concept of an lvalue constant with global scope, deduplicated by its value. It's possible that MSGuidDecl itself, or some of the other similar sorts of things in Clang might be able to be refactored onto this more-generic concept, but there's enough special-case weirdness in MSGuidDecl that I gave up attempting to share code there, at least for now. Finally, for compatibility with libstdc++'s header, I've added a second exception to the "cannot cast from void* to T* in constant evaluation" rule. This seems a bit distasteful, but feels like the best available option. Reviewers: aaron.ballman, erichkeane Differential Revision: https://reviews.llvm.org/D120159 --- clang/docs/LanguageExtensions.rst | 25 ++- clang/docs/ReleaseNotes.rst | 2 + clang/include/clang/AST/ASTContext.h | 9 ++ clang/include/clang/AST/DeclCXX.h | 47 ++++++ clang/include/clang/AST/Expr.h | 17 ++- clang/include/clang/AST/RecursiveASTVisitor.h | 1 + clang/include/clang/AST/Stmt.h | 2 +- clang/include/clang/Basic/DeclNodes.td | 1 + clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 + clang/include/clang/Basic/TokenKinds.def | 1 + clang/include/clang/Sema/Sema.h | 9 +- clang/include/clang/Serialization/ASTBitCodes.h | 7 +- clang/lib/AST/ASTContext.cpp | 17 +++ clang/lib/AST/ASTImporter.cpp | 5 +- clang/lib/AST/DeclBase.cpp | 1 + clang/lib/AST/DeclCXX.cpp | 25 +++ clang/lib/AST/Expr.cpp | 73 ++++++--- clang/lib/AST/ExprClassification.cpp | 13 +- clang/lib/AST/ExprConstant.cpp | 59 ++++++-- clang/lib/CodeGen/CGDecl.cpp | 1 + clang/lib/CodeGen/CGExprConstant.cpp | 3 + clang/lib/CodeGen/CodeGenModule.cpp | 31 ++++ clang/lib/CodeGen/CodeGenModule.h | 6 + clang/lib/Parse/ParseExpr.cpp | 8 +- clang/lib/Sema/Sema.cpp | 5 +- clang/lib/Sema/SemaExpr.cpp | 105 ++++++++++++- clang/lib/Sema/SemaTemplate.cpp | 4 +- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 + clang/lib/Sema/TreeTransform.h | 9 +- clang/lib/Serialization/ASTCommon.cpp | 1 + clang/lib/Serialization/ASTReaderDecl.cpp | 15 ++ clang/lib/Serialization/ASTWriterDecl.cpp | 8 + clang/test/CodeGenCXX/builtin-source-location.cpp | 177 ++++++++++------------ clang/test/SemaCXX/source_location.cpp | 61 ++++---- clang/test/SemaCXX/source_location_err.cpp | 105 +++++++++++++ clang/tools/libclang/CIndex.cpp | 1 + 36 files changed, 666 insertions(+), 198 deletions(-) create mode 100644 clang/test/SemaCXX/source_location_err.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 45fb8ef..0391102 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3377,10 +3377,9 @@ as the first argument to the intrinsic. Source location builtins ------------------------ -Clang provides experimental builtins to support C++ standard library implementation -of ``std::experimental::source_location`` as specified in http://wg21.link/N4600. -With the exception of ``__builtin_COLUMN``, these builtins are also implemented by -GCC. +Clang provides builtins to support C++ standard library implementation +of ``std::source_location`` as specified in C++20. With the exception +of ``__builtin_COLUMN``, these builtins are also implemented by GCC. **Syntax**: @@ -3390,6 +3389,7 @@ GCC. const char *__builtin_FUNCTION(); unsigned __builtin_LINE(); unsigned __builtin_COLUMN(); // Clang only + const std::source_location::__impl *__builtin_source_location(); **Example of use**: @@ -3416,9 +3416,11 @@ GCC. **Description**: -The builtins ``__builtin_LINE``, ``__builtin_FUNCTION``, and ``__builtin_FILE`` return -the values, at the "invocation point", for ``__LINE__``, ``__FUNCTION__``, and -``__FILE__`` respectively. These builtins are constant expressions. +The builtins ``__builtin_LINE``, ``__builtin_FUNCTION``, and ``__builtin_FILE`` +return the values, at the "invocation point", for ``__LINE__``, +``__FUNCTION__``, and ``__FILE__`` respectively. ``__builtin_COLUMN`` similarly +returns the column, though there is no corresponding macro. These builtins are +constant expressions. When the builtins appear as part of a default function argument the invocation point is the location of the caller. When the builtins appear as part of a @@ -3429,6 +3431,15 @@ the invocation point is the same as the location of the builtin. When the invocation point of ``__builtin_FUNCTION`` is not a function scope the empty string is returned. +The builtin ``__builtin_source_location`` returns a pointer to constant static +data of type ``std::source_location::__impl``. This type must have already been +defined, and must contain exactly four fields: ``const char *_M_file_name``, +``const char *_M_function_name``, `` _M_line``, and +`` _M_column``. The fields will be populated in the same +manner as the above four builtins, except that ``_M_function_name`` is populated +with ``__PRETTY_FUNCTION__`` rather than ``__FUNCTION__``. + + Alignment builtins ------------------ Clang provides builtins to support checking and adjusting alignment of diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 951dd68..9e12ad1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -193,6 +193,8 @@ C++20 Feature Support it is called through a template instantiation. This fixes `Issue 54578 `_. +- Implemented `__builtin_source_location()` which enables library support for std::source_location. + C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index bae848f..b00472e 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -314,6 +314,10 @@ class ASTContext : public RefCountedBase { /// Mapping from GUIDs to the corresponding MSGuidDecl. mutable llvm::FoldingSet MSGuidDecls; + /// Mapping from APValues to the corresponding UnnamedGlobalConstantDecl. + mutable llvm::FoldingSet + UnnamedGlobalConstantDecls; + /// Mapping from APValues to the corresponding TemplateParamObjects. mutable llvm::FoldingSet TemplateParamObjectDecls; @@ -3064,6 +3068,11 @@ public: /// GUID value. MSGuidDecl *getMSGuidDecl(MSGuidDeclParts Parts) const; + /// Return a declaration for a uniquified anonymous global constant + /// corresponding to a given APValue. + UnnamedGlobalConstantDecl * + getUnnamedGlobalConstantDecl(QualType Ty, const APValue &Value) const; + /// Return the template parameter object of the given type with the given /// value. TemplateParamObjectDecl *getTemplateParamObjectDecl(QualType T, diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 545f299..f5bd856 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -4206,6 +4206,53 @@ public: static bool classofKind(Kind K) { return K == Decl::MSGuid; } }; +/// An artificial decl, representing a global anonymous constant value which is +/// uniquified by value within a translation unit. +/// +/// These is currently only used to back the LValue returned by +/// __builtin_source_location, but could potentially be used for other similar +/// situations in the future. +class UnnamedGlobalConstantDecl : public ValueDecl, + public Mergeable, + public llvm::FoldingSetNode { + + // The constant value of this global. + APValue Value; + + void anchor() override; + + UnnamedGlobalConstantDecl(DeclContext *DC, QualType T, const APValue &Val); + + static UnnamedGlobalConstantDecl *Create(const ASTContext &C, QualType T, + const APValue &APVal); + static UnnamedGlobalConstantDecl *CreateDeserialized(ASTContext &C, + unsigned ID); + + // Only ASTContext::getUnnamedGlobalConstantDecl and deserialization create + // these. + friend class ASTContext; + friend class ASTReader; + friend class ASTDeclReader; + +public: + /// Print this in a human-readable format. + void printName(llvm::raw_ostream &OS) const override; + + const APValue &getValue() const { return Value; } + + static void Profile(llvm::FoldingSetNodeID &ID, QualType Ty, + const APValue &APVal) { + Ty.Profile(ID); + APVal.Profile(ID); + } + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getType(), getValue()); + } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == Decl::UnnamedGlobalConstant; } +}; + /// Insertion operator for diagnostics. This allows sending an AccessSpecifier /// into a diagnostic with <<. const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index c32e74a..3ecfaa1 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -4680,16 +4680,17 @@ public: }; /// Represents a function call to one of __builtin_LINE(), __builtin_COLUMN(), -/// __builtin_FUNCTION(), or __builtin_FILE(). +/// __builtin_FUNCTION(), __builtin_FILE(), or __builtin_source_location(). class SourceLocExpr final : public Expr { SourceLocation BuiltinLoc, RParenLoc; DeclContext *ParentContext; public: - enum IdentKind { Function, File, Line, Column }; + enum IdentKind { Function, File, Line, Column, SourceLocStruct }; - SourceLocExpr(const ASTContext &Ctx, IdentKind Type, SourceLocation BLoc, - SourceLocation RParenLoc, DeclContext *Context); + SourceLocExpr(const ASTContext &Ctx, IdentKind Type, QualType ResultTy, + SourceLocation BLoc, SourceLocation RParenLoc, + DeclContext *Context); /// Build an empty call expression. explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {} @@ -4706,18 +4707,18 @@ public: return static_cast(SourceLocExprBits.Kind); } - bool isStringType() const { + bool isIntType() const { switch (getIdentKind()) { case File: case Function: - return true; + case SourceLocStruct: + return false; case Line: case Column: - return false; + return true; } llvm_unreachable("unknown source location expression kind"); } - bool isIntType() const LLVM_READONLY { return !isStringType(); } /// If the SourceLocExpr has been resolved return the subexpression /// representing the resolved value. Otherwise return null. diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 5aed81d..0319bc2 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2039,6 +2039,7 @@ DEF_TRAVERSE_DECL(BindingDecl, { DEF_TRAVERSE_DECL(MSPropertyDecl, { TRY_TO(TraverseDeclaratorHelper(D)); }) DEF_TRAVERSE_DECL(MSGuidDecl, {}) +DEF_TRAVERSE_DECL(UnnamedGlobalConstantDecl, {}) DEF_TRAVERSE_DECL(TemplateParamObjectDecl, {}) diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index a32126d..adde424c 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -595,7 +595,7 @@ protected: /// The kind of source location builtin represented by the SourceLocExpr. /// Ex. __builtin_LINE, __builtin_FUNCTION, ect. - unsigned Kind : 2; + unsigned Kind : 3; }; class StmtExprBitfields { diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index f8ad6cf..1ac9b39 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -41,6 +41,7 @@ def Named : DeclNode; def OMPDeclareReduction : DeclNode, DeclContext; def OMPDeclareMapper : DeclNode, DeclContext; def MSGuid : DeclNode; + def UnnamedGlobalConstant : DeclNode; def TemplateParamObject : DeclNode; def Declarator : DeclNode; def Field : DeclNode; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4b35aae..d7a5328 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11556,4 +11556,9 @@ def err_riscv_builtin_requires_extension : Error< "builtin requires at least one of the following extensions support to be enabled : %0">; def err_riscv_builtin_invalid_lmul : Error< "LMUL argument must be in the range [0,3] or [5,7]">; + +def err_std_source_location_impl_not_found : Error< + "'std::source_location::__impl' was not found; it must be defined before '__builtin_source_location' is called">; +def err_std_source_location_impl_malformed : Error< + "'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'">; } // end of sema component. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 1f07cad..5092780d 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -432,6 +432,7 @@ KEYWORD(__builtin_FILE , KEYALL) KEYWORD(__builtin_FUNCTION , KEYALL) KEYWORD(__builtin_LINE , KEYALL) KEYWORD(__builtin_COLUMN , KEYALL) +KEYWORD(__builtin_source_location , KEYCXX) // __builtin_types_compatible_p is a GNU C extension that we handle like a C++ // type trait. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 379e120..6523c30 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1144,6 +1144,10 @@ public: /// The MSVC "_GUID" struct, which is defined in MSVC header files. RecordDecl *MSVCGuidDecl; + /// The C++ "std::source_location::__impl" struct, defined in + /// \. + RecordDecl *StdSourceLocationImplDecl; + /// Caches identifiers/selectors for NSFoundation APIs. std::unique_ptr NSAPIObj; @@ -5684,14 +5688,15 @@ public: TypeSourceInfo *TInfo, SourceLocation RPLoc); // __builtin_LINE(), __builtin_FUNCTION(), __builtin_FILE(), - // __builtin_COLUMN() + // __builtin_COLUMN(), __builtin_source_location() ExprResult ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind, SourceLocation BuiltinLoc, SourceLocation RPLoc); // Build a potentially resolved SourceLocExpr. ExprResult BuildSourceLocExpr(SourceLocExpr::IdentKind Kind, - SourceLocation BuiltinLoc, SourceLocation RPLoc, + QualType ResultTy, SourceLocation BuiltinLoc, + SourceLocation RPLoc, DeclContext *ParentContext); // __null diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 180c8f5..45c771f3 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -41,7 +41,7 @@ namespace serialization { /// Version 4 of AST files also requires that the version control branch and /// revision match exactly, since there is no backward compatibility of /// AST files at this time. -const unsigned VERSION_MAJOR = 16; +const unsigned VERSION_MAJOR = 17; /// AST file minor version number supported by this version of /// Clang. @@ -1504,7 +1504,10 @@ enum DeclCode { /// An OMPDeclareReductionDecl record. DECL_OMP_DECLARE_REDUCTION, - DECL_LAST = DECL_OMP_DECLARE_REDUCTION + /// A UnnamedGlobalConstantDecl record. + DECL_UNNAMED_GLOBAL_CONSTANT, + + DECL_LAST = DECL_UNNAMED_GLOBAL_CONSTANT }; /// Record codes for each kind of statement or expression. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 77b9edc..eeaff7d 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -11875,6 +11875,23 @@ ASTContext::getMSGuidDecl(MSGuidDecl::Parts Parts) const { return New; } +UnnamedGlobalConstantDecl * +ASTContext::getUnnamedGlobalConstantDecl(QualType Ty, + const APValue &APVal) const { + llvm::FoldingSetNodeID ID; + UnnamedGlobalConstantDecl::Profile(ID, Ty, APVal); + + void *InsertPos; + if (UnnamedGlobalConstantDecl *Existing = + UnnamedGlobalConstantDecls.FindNodeOrInsertPos(ID, InsertPos)) + return Existing; + + UnnamedGlobalConstantDecl *New = + UnnamedGlobalConstantDecl::Create(*this, Ty, APVal); + UnnamedGlobalConstantDecls.InsertNode(New, InsertPos); + return New; +} + TemplateParamObjectDecl * ASTContext::getTemplateParamObjectDecl(QualType T, const APValue &V) const { assert(T->isRecordType() && "template param object of unexpected type"); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index a5b6e3f..5661e55 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -6669,6 +6669,7 @@ ExpectedStmt ASTNodeImporter::VisitExpr(Expr *E) { ExpectedStmt ASTNodeImporter::VisitSourceLocExpr(SourceLocExpr *E) { Error Err = Error::success(); + auto ToType = importChecked(Err, E->getType()); auto BLoc = importChecked(Err, E->getBeginLoc()); auto RParenLoc = importChecked(Err, E->getEndLoc()); if (Err) @@ -6678,8 +6679,8 @@ ExpectedStmt ASTNodeImporter::VisitSourceLocExpr(SourceLocExpr *E) { return ParentContextOrErr.takeError(); return new (Importer.getToContext()) - SourceLocExpr(Importer.getToContext(), E->getIdentKind(), BLoc, RParenLoc, - *ParentContextOrErr); + SourceLocExpr(Importer.getToContext(), E->getIdentKind(), ToType, BLoc, + RParenLoc, *ParentContextOrErr); } ExpectedStmt ASTNodeImporter::VisitVAArgExpr(VAArgExpr *E) { diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index ccadbdd..d5ad636 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -838,6 +838,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case ExternCContext: case Decomposition: case MSGuid: + case UnnamedGlobalConstant: case TemplateParamObject: case UsingDirective: diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 400709d..b3f3efe 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -3363,6 +3363,31 @@ APValue &MSGuidDecl::getAsAPValue() const { return APVal; } +void UnnamedGlobalConstantDecl::anchor() {} + +UnnamedGlobalConstantDecl::UnnamedGlobalConstantDecl(DeclContext *DC, + QualType Ty, + const APValue &Value) + : ValueDecl(Decl::UnnamedGlobalConstant, DC, SourceLocation(), + DeclarationName(), Ty), + Value(Value) {} + +UnnamedGlobalConstantDecl * +UnnamedGlobalConstantDecl::Create(const ASTContext &C, QualType T, + const APValue &Value) { + DeclContext *DC = C.getTranslationUnitDecl(); + return new (C, DC) UnnamedGlobalConstantDecl(DC, T, Value); +} + +UnnamedGlobalConstantDecl * +UnnamedGlobalConstantDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + return new (C, ID) UnnamedGlobalConstantDecl(nullptr, QualType(), APValue()); +} + +void UnnamedGlobalConstantDecl::printName(llvm::raw_ostream &OS) const { + OS << "unnamed-global-constant"; +} + static const char *getAccessName(AccessSpecifier AS) { switch (AS) { case AS_none: diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 49765b8..2a08613 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2136,26 +2136,11 @@ bool BinaryOperator::isNullPointerArithmeticExtension(ASTContext &Ctx, return true; } -static QualType getDecayedSourceLocExprType(const ASTContext &Ctx, - SourceLocExpr::IdentKind Kind) { - switch (Kind) { - case SourceLocExpr::File: - case SourceLocExpr::Function: { - QualType ArrTy = Ctx.getStringLiteralArrayType(Ctx.CharTy, 0); - return Ctx.getPointerType(ArrTy->getAsArrayTypeUnsafe()->getElementType()); - } - case SourceLocExpr::Line: - case SourceLocExpr::Column: - return Ctx.UnsignedIntTy; - } - llvm_unreachable("unhandled case"); -} - SourceLocExpr::SourceLocExpr(const ASTContext &Ctx, IdentKind Kind, - SourceLocation BLoc, SourceLocation RParenLoc, + QualType ResultTy, SourceLocation BLoc, + SourceLocation RParenLoc, DeclContext *ParentContext) - : Expr(SourceLocExprClass, getDecayedSourceLocExprType(Ctx, Kind), - VK_PRValue, OK_Ordinary), + : Expr(SourceLocExprClass, ResultTy, VK_PRValue, OK_Ordinary), BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext) { SourceLocExprBits.Kind = Kind; setDependence(ExprDependence::None); @@ -2171,6 +2156,8 @@ StringRef SourceLocExpr::getBuiltinStr() const { return "__builtin_LINE"; case Column: return "__builtin_COLUMN"; + case SourceLocStruct: + return "__builtin_source_location"; } llvm_unreachable("unexpected IdentKind!"); } @@ -2207,7 +2194,7 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, return MakeStringLiteral(Path); } case SourceLocExpr::Function: { - const Decl *CurDecl = dyn_cast_or_null(Context); + const auto *CurDecl = dyn_cast(Context); return MakeStringLiteral( CurDecl ? PredefinedExpr::ComputeName(PredefinedExpr::Function, CurDecl) : std::string("")); @@ -2220,6 +2207,54 @@ APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, : PLoc.getColumn(); return APValue(IntVal); } + case SourceLocExpr::SourceLocStruct: { + // Fill in a std::source_location::__impl structure, by creating an + // artificial file-scoped CompoundLiteralExpr, and returning a pointer to + // that. + const CXXRecordDecl *ImplDecl = getType()->getPointeeCXXRecordDecl(); + assert(ImplDecl); + + // Construct an APValue for the __impl struct, and get or create a Decl + // corresponding to that. Note that we've already verified that the shape of + // the ImplDecl type is as expected. + + APValue Value(APValue::UninitStruct(), 0, 4); + for (FieldDecl *F : ImplDecl->fields()) { + StringRef Name = F->getName(); + if (Name == "_M_file_name") { + SmallString<256> Path(PLoc.getFilename()); + Ctx.getLangOpts().remapPathPrefix(Path); + Value.getStructField(F->getFieldIndex()) = MakeStringLiteral(Path); + } else if (Name == "_M_function_name") { + // Note: this emits the PrettyFunction name -- different than what + // __builtin_FUNCTION() above returns! + const auto *CurDecl = dyn_cast(Context); + Value.getStructField(F->getFieldIndex()) = MakeStringLiteral( + CurDecl && !isa(CurDecl) + ? StringRef(PredefinedExpr::ComputeName( + PredefinedExpr::PrettyFunction, CurDecl)) + : ""); + } else if (Name == "_M_line") { + QualType Ty = F->getType(); + llvm::APSInt IntVal(Ctx.getIntWidth(Ty), + Ty->hasUnsignedIntegerRepresentation()); + IntVal = PLoc.getLine(); + Value.getStructField(F->getFieldIndex()) = APValue(IntVal); + } else if (Name == "_M_column") { + QualType Ty = F->getType(); + llvm::APSInt IntVal(Ctx.getIntWidth(Ty), + Ty->hasUnsignedIntegerRepresentation()); + IntVal = PLoc.getColumn(); + Value.getStructField(F->getFieldIndex()) = APValue(IntVal); + } + } + + UnnamedGlobalConstantDecl *GV = + Ctx.getUnnamedGlobalConstantDecl(getType()->getPointeeType(), Value); + + return APValue(GV, CharUnits::Zero(), ArrayRef{}, + false); + } } llvm_unreachable("unhandled case"); } diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 6998e28..6c122ca 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -465,14 +465,11 @@ static Cl::Kinds ClassifyDecl(ASTContext &Ctx, const Decl *D) { islvalue = NTTParm->getType()->isReferenceType() || NTTParm->getType()->isRecordType(); else - islvalue = isa(D) || isa(D) || - isa(D) || - isa(D) || - isa(D) || - isa(D) || - (Ctx.getLangOpts().CPlusPlus && - (isa(D) || isa(D) || - isa(D))); + islvalue = + isa(D) || + (Ctx.getLangOpts().CPlusPlus && + (isa(D))); return islvalue ? Cl::CL_LValue : Cl::CL_PRValue; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 0930bfd..6c2447d 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1978,7 +1978,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) { return true; // ... the address of a function, // ... the address of a GUID [MS extension], - return isa(D) || isa(D); + // ... the address of an unnamed global constant + return isa(D); } if (B.is() || B.is()) @@ -2013,6 +2014,10 @@ static bool IsGlobalLValue(APValue::LValueBase B) { // Block variables at global or local static scope. case Expr::BlockExprClass: return !cast(E)->getBlockDecl()->hasCaptures(); + // The APValue generated from a __builtin_source_location will be emitted as a + // literal. + case Expr::SourceLocExprClass: + return true; case Expr::ImplicitValueInitExprClass: // FIXME: // We can never form an lvalue with an implicit value initialization as its @@ -4024,6 +4029,16 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, return CompleteObject(LVal.Base, &V, GD->getType()); } + // Allow reading the APValue from an UnnamedGlobalConstantDecl. + if (auto *GCD = dyn_cast(D)) { + if (isModification(AK)) { + Info.FFDiag(E, diag::note_constexpr_modify_global); + return CompleteObject(); + } + return CompleteObject(LVal.Base, const_cast(&GCD->getValue()), + GCD->getType()); + } + // Allow reading from template parameter objects. if (auto *TPO = dyn_cast(D)) { if (isModification(AK)) { @@ -8175,7 +8190,8 @@ static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info, bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { const NamedDecl *D = E->getDecl(); - if (isa(D)) + if (isa(D)) return Success(cast(D)); if (const VarDecl *VD = dyn_cast(D)) return VisitVarDecl(E, VD); @@ -8715,7 +8731,7 @@ public: bool VisitCXXNewExpr(const CXXNewExpr *E); bool VisitSourceLocExpr(const SourceLocExpr *E) { - assert(E->isStringType() && "SourceLocExpr isn't a pointer type?"); + assert(!E->isIntType() && "SourceLocExpr isn't a pointer type?"); APValue LValResult = E->EvaluateInContext( Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getDefaultExpr()); Result.setFrom(Info.Ctx, LValResult); @@ -8780,6 +8796,22 @@ bool PointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) { return evaluateLValue(E->getSubExpr(), Result); } +// Is the provided decl 'std::source_location::current'? +static bool IsDeclSourceLocationCurrent(const FunctionDecl *FD) { + if (!FD) + return false; + const IdentifierInfo *FnII = FD->getIdentifier(); + if (!FnII || !FnII->isStr("current")) + return false; + + const auto *RD = dyn_cast(FD->getParent()); + if (!RD) + return false; + + const IdentifierInfo *ClassII = RD->getIdentifier(); + return RD->isInStdNamespace() && ClassII && ClassII->isStr("source_location"); +} + bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { const Expr *SubExpr = E->getSubExpr(); @@ -8797,14 +8829,23 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { // permitted in constant expressions in C++11. Bitcasts from cv void* are // also static_casts, but we disallow them as a resolution to DR1312. if (!E->getType()->isVoidPointerType()) { - if (!Result.InvalidBase && !Result.Designator.Invalid && + // In some circumstances, we permit casting from void* to cv1 T*, when the + // actual pointee object is actually a cv2 T. + bool VoidPtrCastMaybeOK = + !Result.InvalidBase && !Result.Designator.Invalid && !Result.IsNullPtr && Info.Ctx.hasSameUnqualifiedType(Result.Designator.getType(Info.Ctx), - E->getType()->getPointeeType()) && - Info.getStdAllocatorCaller("allocate")) { - // Inside a call to std::allocator::allocate and friends, we permit - // casting from void* back to cv1 T* for a pointer that points to a - // cv2 T. + E->getType()->getPointeeType()); + // 1. We'll allow it in std::allocator::allocate, and anything which that + // calls. + // 2. We'll allow it in the body of std::source_location:current. This is + // necessary for libstdc++'s , which gave its + // parameter the type void*, and cast from that back to `const __impl*` + // in the body. (Fixed for new versions in gcc.gnu.org/PR104602). + if (VoidPtrCastMaybeOK && + (Info.getStdAllocatorCaller("allocate") || + IsDeclSourceLocationCurrent(Info.CurrentCall->Callee))) { + // Permitted. } else { Result.Designator.setInvalid(); if (SubExpr->getType()->isVoidPointerType()) diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index e54101f..1f5bdec 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -118,6 +118,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Label: // __label__ x; case Decl::Import: case Decl::MSGuid: // __declspec(uuid("...")) + case Decl::UnnamedGlobalConstant: case Decl::TemplateParamObject: case Decl::OMPThreadPrivate: case Decl::OMPAllocate: diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index ac4b4d1..6397ff5 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1909,6 +1909,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { if (auto *GD = dyn_cast(D)) return CGM.GetAddrOfMSGuidDecl(GD); + if (auto *GCD = dyn_cast(D)) + return CGM.GetAddrOfUnnamedGlobalConstantDecl(GCD); + if (auto *TPO = dyn_cast(D)) return CGM.GetAddrOfTemplateParamObject(TPO); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index c90e042..5fe1495 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2905,6 +2905,37 @@ ConstantAddress CodeGenModule::GetAddrOfMSGuidDecl(const MSGuidDecl *GD) { return ConstantAddress(Addr, Ty, Alignment); } +ConstantAddress CodeGenModule::GetAddrOfUnnamedGlobalConstantDecl( + const UnnamedGlobalConstantDecl *GCD) { + CharUnits Alignment = getContext().getTypeAlignInChars(GCD->getType()); + + llvm::GlobalVariable **Entry = nullptr; + Entry = &UnnamedGlobalConstantDeclMap[GCD]; + if (*Entry) + return ConstantAddress(*Entry, (*Entry)->getValueType(), Alignment); + + ConstantEmitter Emitter(*this); + llvm::Constant *Init; + + const APValue &V = GCD->getValue(); + + assert(!V.isAbsent()); + Init = Emitter.emitForInitializer(V, GCD->getType().getAddressSpace(), + GCD->getType()); + + auto *GV = new llvm::GlobalVariable(getModule(), Init->getType(), + /*isConstant=*/true, + llvm::GlobalValue::PrivateLinkage, Init, + ".constant"); + GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + GV->setAlignment(Alignment.getAsAlign()); + + Emitter.finalize(GV); + + *Entry = GV; + return ConstantAddress(GV, GV->getValueType(), Alignment); +} + ConstantAddress CodeGenModule::GetAddrOfTemplateParamObject( const TemplateParamObjectDecl *TPO) { StringRef Name = getMangledName(TPO); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index b04393e..d8ec8f0 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -406,6 +406,8 @@ private: llvm::StringMap CFConstantStringMap; llvm::DenseMap ConstantStringMap; + llvm::DenseMap + UnnamedGlobalConstantDeclMap; llvm::DenseMap StaticLocalDeclMap; llvm::DenseMap StaticLocalDeclGuardMap; llvm::DenseMap MaterializedGlobalTemporaryMap; @@ -875,6 +877,10 @@ public: /// Get the address of a GUID. ConstantAddress GetAddrOfMSGuidDecl(const MSGuidDecl *GD); + /// Get the address of a UnnamedGlobalConstant + ConstantAddress + GetAddrOfUnnamedGlobalConstantDecl(const UnnamedGlobalConstantDecl *GCD); + /// Get the address of a template parameter object. ConstantAddress GetAddrOfTemplateParamObject(const TemplateParamObjectDecl *TPO); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index fefb293..54ba115 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -791,6 +791,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback { /// [GNU] '__builtin_FUNCTION' '(' ')' /// [GNU] '__builtin_LINE' '(' ')' /// [CLANG] '__builtin_COLUMN' '(' ')' +/// [GNU] '__builtin_source_location' '(' ')' /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' /// [GNU] '__null' /// [OBJC] '[' objc-message-expr ']' @@ -1303,6 +1304,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw___builtin_FILE: case tok::kw___builtin_FUNCTION: case tok::kw___builtin_LINE: + case tok::kw___builtin_source_location: if (NotPrimaryExpression) *NotPrimaryExpression = true; // This parses the complete suffix; we can return early. @@ -2508,6 +2510,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { /// [GNU] '__builtin_FUNCTION' '(' ')' /// [GNU] '__builtin_LINE' '(' ')' /// [CLANG] '__builtin_COLUMN' '(' ')' +/// [GNU] '__builtin_source_location' '(' ')' /// [OCL] '__builtin_astype' '(' assignment-expression ',' type-name ')' /// /// [GNU] offsetof-member-designator: @@ -2730,7 +2733,8 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { case tok::kw___builtin_COLUMN: case tok::kw___builtin_FILE: case tok::kw___builtin_FUNCTION: - case tok::kw___builtin_LINE: { + case tok::kw___builtin_LINE: + case tok::kw___builtin_source_location: { // Attempt to consume the r-paren. if (Tok.isNot(tok::r_paren)) { Diag(Tok, diag::err_expected) << tok::r_paren; @@ -2747,6 +2751,8 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { return SourceLocExpr::Line; case tok::kw___builtin_COLUMN: return SourceLocExpr::Column; + case tok::kw___builtin_source_location: + return SourceLocExpr::SourceLocStruct; default: llvm_unreachable("invalid keyword"); } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 52f38c0..ce104f3 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -202,8 +202,9 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp), StdExperimentalNamespaceCache(nullptr), StdInitializerList(nullptr), StdCoroutineTraitsCache(nullptr), CXXTypeInfoDecl(nullptr), - MSVCGuidDecl(nullptr), NSNumberDecl(nullptr), NSValueDecl(nullptr), - NSStringDecl(nullptr), StringWithUTF8StringMethod(nullptr), + MSVCGuidDecl(nullptr), StdSourceLocationImplDecl(nullptr), + NSNumberDecl(nullptr), NSValueDecl(nullptr), NSStringDecl(nullptr), + StringWithUTF8StringMethod(nullptr), ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr), ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr), DictionaryWithObjectsMethod(nullptr), GlobalNewDeleteDeclared(false), diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index c5ede6e..c0a1ffc 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3439,6 +3439,10 @@ ExprResult Sema::BuildDeclarationNameExpr( valueKind = VK_LValue; break; + case Decl::UnnamedGlobalConstant: + valueKind = VK_LValue; + break; + case Decl::CXXMethod: // If we're referring to a method with an __unknown_anytype // result type, make the entire expression __unknown_anytype. @@ -14125,8 +14129,8 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { return MPTy; } } - } else if (!isa(dcl) && !isa(dcl) && - !isa(dcl) && !isa(dcl)) + } else if (!isa(dcl)) llvm_unreachable("Unknown/unexpected decl type"); } @@ -16310,18 +16314,111 @@ ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) { return new (Context) GNUNullExpr(Ty, TokenLoc); } +static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { + CXXRecordDecl *ImplDecl = nullptr; + + // Fetch the std::source_location::__impl decl. + if (NamespaceDecl *Std = S.getStdNamespace()) { + LookupResult ResultSL(S, &S.PP.getIdentifierTable().get("source_location"), + Loc, Sema::LookupOrdinaryName); + if (S.LookupQualifiedName(ResultSL, Std)) { + if (auto *SLDecl = ResultSL.getAsSingle()) { + LookupResult ResultImpl(S, &S.PP.getIdentifierTable().get("__impl"), + Loc, Sema::LookupOrdinaryName); + if ((SLDecl->isCompleteDefinition() || SLDecl->isBeingDefined()) && + S.LookupQualifiedName(ResultImpl, SLDecl)) { + ImplDecl = ResultImpl.getAsSingle(); + } + } + } + } + + if (!ImplDecl || !ImplDecl->isCompleteDefinition()) { + S.Diag(Loc, diag::err_std_source_location_impl_not_found); + return nullptr; + } + + // Verify that __impl is a trivial struct type, with no base classes, and with + // only the four expected fields. + if (ImplDecl->isUnion() || !ImplDecl->isStandardLayout() || + ImplDecl->getNumBases() != 0) { + S.Diag(Loc, diag::err_std_source_location_impl_malformed); + return nullptr; + } + + unsigned Count = 0; + for (FieldDecl *F : ImplDecl->fields()) { + StringRef Name = F->getName(); + + if (Name == "_M_file_name") { + if (F->getType() != + S.Context.getPointerType(S.Context.CharTy.withConst())) + break; + Count++; + } else if (Name == "_M_function_name") { + if (F->getType() != + S.Context.getPointerType(S.Context.CharTy.withConst())) + break; + Count++; + } else if (Name == "_M_line") { + if (!F->getType()->isIntegerType()) + break; + Count++; + } else if (Name == "_M_column") { + if (!F->getType()->isIntegerType()) + break; + Count++; + } else { + Count = 100; // invalid + break; + } + } + if (Count != 4) { + S.Diag(Loc, diag::err_std_source_location_impl_malformed); + return nullptr; + } + + return ImplDecl; +} + ExprResult Sema::ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind, SourceLocation BuiltinLoc, SourceLocation RPLoc) { - return BuildSourceLocExpr(Kind, BuiltinLoc, RPLoc, CurContext); + QualType ResultTy; + switch (Kind) { + case SourceLocExpr::File: + case SourceLocExpr::Function: { + QualType ArrTy = Context.getStringLiteralArrayType(Context.CharTy, 0); + ResultTy = + Context.getPointerType(ArrTy->getAsArrayTypeUnsafe()->getElementType()); + break; + } + case SourceLocExpr::Line: + case SourceLocExpr::Column: + ResultTy = Context.UnsignedIntTy; + break; + case SourceLocExpr::SourceLocStruct: + if (!StdSourceLocationImplDecl) { + StdSourceLocationImplDecl = + LookupStdSourceLocationImpl(*this, BuiltinLoc); + if (!StdSourceLocationImplDecl) + return ExprError(); + } + ResultTy = Context.getPointerType( + Context.getRecordType(StdSourceLocationImplDecl).withConst()); + break; + } + + return BuildSourceLocExpr(Kind, ResultTy, BuiltinLoc, RPLoc, CurContext); } ExprResult Sema::BuildSourceLocExpr(SourceLocExpr::IdentKind Kind, + QualType ResultTy, SourceLocation BuiltinLoc, SourceLocation RPLoc, DeclContext *ParentContext) { return new (Context) - SourceLocExpr(Context, Kind, BuiltinLoc, RPLoc, ParentContext); + SourceLocExpr(Context, Kind, ResultTy, BuiltinLoc, RPLoc, ParentContext); } bool Sema::CheckConversionToObjCLiteral(QualType DstType, Expr *&Exp, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index b34df75..bf65f11 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -7009,7 +7009,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // -- a predefined __func__ variable APValue::LValueBase Base = Value.getLValueBase(); auto *VD = const_cast(Base.dyn_cast()); - if (Base && (!VD || isa(VD))) { + if (Base && + (!VD || + isa(VD))) { Diag(Arg->getBeginLoc(), diag::err_template_arg_not_decl_ref) << Arg->getSourceRange(); return ExprError(); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index e6e6b3b..a914ae2 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -867,6 +867,11 @@ Decl *TemplateDeclInstantiator::VisitMSGuidDecl(MSGuidDecl *D) { llvm_unreachable("GUID declaration cannot be instantiated"); } +Decl *TemplateDeclInstantiator::VisitUnnamedGlobalConstantDecl( + UnnamedGlobalConstantDecl *D) { + llvm_unreachable("UnnamedGlobalConstantDecl cannot be instantiated"); +} + Decl *TemplateDeclInstantiator::VisitTemplateParamObjectDecl( TemplateParamObjectDecl *D) { llvm_unreachable("template parameter objects cannot be instantiated"); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index e27ac97..773cc66 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3386,10 +3386,11 @@ public: /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. ExprResult RebuildSourceLocExpr(SourceLocExpr::IdentKind Kind, - SourceLocation BuiltinLoc, + QualType ResultTy, SourceLocation BuiltinLoc, SourceLocation RPLoc, DeclContext *ParentContext) { - return getSema().BuildSourceLocExpr(Kind, BuiltinLoc, RPLoc, ParentContext); + return getSema().BuildSourceLocExpr(Kind, ResultTy, BuiltinLoc, RPLoc, + ParentContext); } /// Build a new Objective-C boxed expression. @@ -11653,8 +11654,8 @@ ExprResult TreeTransform::TransformSourceLocExpr(SourceLocExpr *E) { if (!getDerived().AlwaysRebuild() && !NeedRebuildFunc) return E; - return getDerived().RebuildSourceLocExpr(E->getIdentKind(), E->getBeginLoc(), - E->getEndLoc(), + return getDerived().RebuildSourceLocExpr(E->getIdentKind(), E->getType(), + E->getBeginLoc(), E->getEndLoc(), getSema().CurContext); } diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index c60f87a..673c566 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -391,6 +391,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Field: case Decl::MSProperty: case Decl::MSGuid: + case Decl::UnnamedGlobalConstant: case Decl::TemplateParamObject: case Decl::ObjCIvar: case Decl::ObjCAtDefsField: diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 0889f5a..40208eb 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -370,6 +370,7 @@ namespace clang { void VisitFieldDecl(FieldDecl *FD); void VisitMSPropertyDecl(MSPropertyDecl *FD); void VisitMSGuidDecl(MSGuidDecl *D); + void VisitUnnamedGlobalConstantDecl(UnnamedGlobalConstantDecl *D); void VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D); void VisitIndirectFieldDecl(IndirectFieldDecl *FD); RedeclarableResult VisitVarDeclImpl(VarDecl *D); @@ -1428,6 +1429,17 @@ void ASTDeclReader::VisitMSGuidDecl(MSGuidDecl *D) { Reader.getContext().setPrimaryMergedDecl(D, Existing->getCanonicalDecl()); } +void ASTDeclReader::VisitUnnamedGlobalConstantDecl( + UnnamedGlobalConstantDecl *D) { + VisitValueDecl(D); + D->Value = Record.readAPValue(); + + // Add this to the AST context's lookup structure, and merge if needed. + if (UnnamedGlobalConstantDecl *Existing = + Reader.getContext().UnnamedGlobalConstantDecls.GetOrInsertNode(D)) + Reader.getContext().setPrimaryMergedDecl(D, Existing->getCanonicalDecl()); +} + void ASTDeclReader::VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D) { VisitValueDecl(D); D->Value = Record.readAPValue(); @@ -3709,6 +3721,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_MS_GUID: D = MSGuidDecl::CreateDeserialized(Context, ID); break; + case DECL_UNNAMED_GLOBAL_CONSTANT: + D = UnnamedGlobalConstantDecl::CreateDeserialized(Context, ID); + break; case DECL_TEMPLATE_PARAM_OBJECT: D = TemplateParamObjectDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index f2f9d39..39ad965 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -96,6 +96,7 @@ namespace clang { void VisitFieldDecl(FieldDecl *D); void VisitMSPropertyDecl(MSPropertyDecl *D); void VisitMSGuidDecl(MSGuidDecl *D); + void VisitUnnamedGlobalConstantDecl(UnnamedGlobalConstantDecl *D); void VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D); void VisitIndirectFieldDecl(IndirectFieldDecl *D); void VisitVarDecl(VarDecl *D); @@ -966,6 +967,13 @@ void ASTDeclWriter::VisitMSGuidDecl(MSGuidDecl *D) { Code = serialization::DECL_MS_GUID; } +void ASTDeclWriter::VisitUnnamedGlobalConstantDecl( + UnnamedGlobalConstantDecl *D) { + VisitValueDecl(D); + Record.AddAPValue(D->getValue()); + Code = serialization::DECL_UNNAMED_GLOBAL_CONSTANT; +} + void ASTDeclWriter::VisitTemplateParamObjectDecl(TemplateParamObjectDecl *D) { VisitValueDecl(D); Record.AddAPValue(D->getValue()); diff --git a/clang/test/CodeGenCXX/builtin-source-location.cpp b/clang/test/CodeGenCXX/builtin-source-location.cpp index 2c5084c..beff201 100644 --- a/clang/test/CodeGenCXX/builtin-source-location.cpp +++ b/clang/test/CodeGenCXX/builtin-source-location.cpp @@ -10,71 +10,63 @@ void testRemap() { #line 8 "builtin-source-location.cpp" -struct source_location { -private: - unsigned int __m_line = 0; - unsigned int __m_col = 0; - const char *__m_file = nullptr; - const char *__m_func = nullptr; - +namespace std { +class source_location { public: - constexpr void set(unsigned l, unsigned c, const char *f, const char *func) { - __m_line = l; - __m_col = c; - __m_file = f; - __m_func = func; - } - static constexpr source_location current( - unsigned int __line = __builtin_LINE(), - unsigned int __col = __builtin_COLUMN(), - const char *__file = __builtin_FILE(), - const char *__func = __builtin_FUNCTION()) noexcept { + static constexpr source_location current(const void *__p = __builtin_source_location()) noexcept { source_location __loc; - __loc.set(__line, __col, __file, __func); + __loc.__m_impl = static_cast(__p); return __loc; } - static source_location bad_current( - unsigned int __line = __builtin_LINE(), - unsigned int __col = __builtin_COLUMN(), - const char *__file = __builtin_FILE(), - const char *__func = __builtin_FUNCTION()) noexcept { - source_location __loc; - __loc.set(__line, __col, __file, __func); - return __loc; + static source_location bad_current(const void *__p = __builtin_source_location()) noexcept { + return current(__p); } constexpr source_location() = default; constexpr source_location(source_location const &) = default; - constexpr unsigned int line() const noexcept { return __m_line; } - constexpr unsigned int column() const noexcept { return __m_col; } - constexpr const char *file() const noexcept { return __m_file; } - constexpr const char *function() const noexcept { return __m_func; } + constexpr unsigned int line() const noexcept { return __m_impl->_M_line; } + constexpr unsigned int column() const noexcept { return __m_impl->_M_column; } + constexpr const char *file() const noexcept { return __m_impl->_M_file_name; } + constexpr const char *function() const noexcept { return __m_impl->_M_function_name; } + +private: + // Note: The type name "std::source_location::__impl", and its constituent + // field-names are required by __builtin_source_location(). + struct __impl { + const char *_M_file_name; + const char *_M_function_name; + unsigned _M_line; + unsigned _M_column; + }; + const __impl *__m_impl = nullptr; }; +} // namespace std -using SL = source_location; +using SL = std::source_location; extern "C" int sink(...); - // RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-GLOBAL-ONE // // CHECK-GLOBAL-ONE-DAG: @[[FILE:.*]] = {{.*}}c"test_const_init.cpp\00" // CHECK-GLOBAL-ONE-DAG: @[[FUNC:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 -// -// CHECK-GLOBAL-ONE: @const_init_global ={{.*}} global %struct.source_location { i32 1000, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] +// CHECK-GLOBAL-ONE-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 1000, i32 {{[0-9]+}} }, align 8 +// CHECK-GLOBAL-ONE: @const_init_global ={{.*}} global %"class.std::source_location" { %"struct.std::source_location::__impl"* @[[IMPL]] }, align 8 #line 1000 "test_const_init.cpp" SL const_init_global = SL::current(); // RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-GLOBAL-TWO // -// CHECK-GLOBAL-TWO-DAG: @runtime_init_global ={{.*}} global %struct.source_location zeroinitializer, align 8 +// CHECK-GLOBAL-TWO-DAG: @runtime_init_global ={{.*}} global %"class.std::source_location" zeroinitializer, align 8 // // CHECK-GLOBAL-TWO-DAG: @[[FILE:.*]] = {{.*}}c"test_runtime_init.cpp\00" // CHECK-GLOBAL-TWO-DAG: @[[FUNC:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +// CHECK-GLOBAL-TWO-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 1100, i32 {{[0-9]+}} }, align 8 // // CHECK-GLOBAL-TWO: define internal void @__cxx_global_var_init() // CHECK-GLOBAL-TWO-NOT: ret -// CHECK-GLOBAL-TWO: call void @_ZN15source_location11bad_currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 @runtime_init_global, -// CHECK-GLOBAL-TWO-SAME: i32 noundef 1100, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], +// CHECK-GLOBAL-TWO: %call = call %"struct.std::source_location::__impl"* @_ZNSt15source_location11bad_currentEPKv({{.*}} @[[IMPL]] +// CHECK-GLOBAL-TWO: store %"struct.std::source_location::__impl"* %call, {{.*}} @runtime_init_global + #line 1100 "test_runtime_init.cpp" SL runtime_init_global = SL::bad_current(); @@ -83,11 +75,11 @@ extern "C" void test_function() { // RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-LOCAL-ONE // // CHECK-LOCAL-ONE-DAG: @[[FILE:.*]] = {{.*}}c"test_current.cpp\00" -// CHECK-LOCAL-ONE-DAG: @[[FUNC:.*]] = {{.*}}c"test_function\00" +// CHECK-LOCAL-ONE-DAG: @[[FUNC:.*]] = {{.*}}c"void test_function()\00" +// CHECK-LOCAL-ONE-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 2100, i32 {{[0-9]+}} }, align 8 // -// CHECK-LOCAL-ONE: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %local, -// CHECK-LOCAL-ONE-SAME: i32 noundef 2100, i32 noundef {{[0-9]+}}, -// CHECK-LOCAL-ONE-SAME: {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], +// CHECK-LOCAL-ONE: define {{.*}} @test_function +// CHECK-LOCAL-ONE: call %"struct.std::source_location::__impl"* @_ZNSt15source_location7currentEPKv({{.*}} @[[IMPL]] #line 2100 "test_current.cpp" SL local = SL::current(); } @@ -106,13 +98,14 @@ struct TestInit { // CHECK-CTOR-GLOBAL-DAG: @GlobalInitVal ={{.*}} global %struct.TestInit zeroinitializer, align 8 // CHECK-CTOR-GLOBAL-DAG: @[[FILE:.*]] = {{.*}}c"GlobalInitVal.cpp\00" // CHECK-CTOR-GLOBAL-DAG: @[[FUNC:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +// CHECK-CTOR-GLOBAL-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 3400, i32 {{[0-9]+}} }, align 8 // // CHECK-CTOR-GLOBAL: define internal void @__cxx_global_var_init.{{[0-9]+}}() // CHECK-CTOR-GLOBAL-NOT: ret // -// CHECK-CTOR-GLOBAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[TMP_ONE:[^,]*]], -// CHECK-CTOR-GLOBAL-SAME: i32 noundef 3400, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], -// CHECK-CTOR-GLOBAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* {{[^,]*}} @GlobalInitVal, %struct.source_location* {{.*}}%[[TMP_ONE]]) +// CHECK-CTOR-GLOBAL: call %"struct.std::source_location::__impl"* @_ZNSt15source_location7currentEPKv({{.*}} @[[IMPL]] +// CHECK-CTOR-GLOBAL-NOT: ret +// CHECK-CTOR-GLOBAL: call void @_ZN8TestInitC1ESt15source_location(%struct.TestInit* {{[^,]*}} @GlobalInitVal, %"struct.std::source_location::__impl"* #line 3400 "GlobalInitVal.cpp" TestInit GlobalInitVal; @@ -120,14 +113,16 @@ extern "C" void test_init_function() { // RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-CTOR-LOCAL // // CHECK-CTOR-LOCAL-DAG: @[[FILE:.*]] = {{.*}}c"LocalInitVal.cpp\00" -// CHECK-CTOR-LOCAL-DAG: @[[FUNC:.*]] = {{.*}}c"test_init_function\00" +// CHECK-CTOR-LOCAL-DAG: @[[FUNC:.*]] = {{.*}}c"void test_init_function()\00" +// CHECK-CTOR-LOCAL-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 3500, i32 {{[0-9]+}} }, align 8 // // CHECK-CTOR-LOCAL: define{{.*}} void @test_init_function() // CHECK-CTOR-LOCAL-NOT: ret // -// CHECK-CTOR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[TMP:[^,]*]], -// CHECK-CTOR-LOCAL-SAME: i32 noundef 3500, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], -// CHECK-CTOR-LOCAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* {{[^,]*}} %init_local, %struct.source_location* {{.*}}%[[TMP]]) +// CHECK-CTOR-LOCAL: call %"struct.std::source_location::__impl"* @_ZNSt15source_location7currentEPKv({{.*}} @[[IMPL]] +// CHECK-CTOR-LOCAL-NOT: ret +// CHECK-CTOR-LOCAL: call void @_ZN8TestInitC1ESt15source_location(%struct.TestInit* {{[^,]*}} %init_local, %"struct.std::source_location::__impl"* + #line 3500 "LocalInitVal.cpp" TestInit init_local; sink(init_local); @@ -144,26 +139,30 @@ struct TestInitConstexpr { // RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-CONSTEXPR-T2 // // CHECK-CONSTEXPR-T2-DAG: @[[FILE_INIT:.*]] = {{.*}}c"ConstexprCtor.cpp\00" -// CHECK-CONSTEXPR-T2-DAG: @[[FUNC_INIT:.*]] = {{.*}}c"TestInitConstexpr\00" +// CHECK-CONSTEXPR-T2-DAG: @[[FUNC_INIT:.*]] = {{.*}}c"TestInitConstexpr::TestInitConstexpr(SL)\00" +// CHECK-CONSTEXPR-T2-DAG: @[[IMPL_INIT:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE_INIT]], {{[^@]*}}@[[FUNC_INIT]], {{.*}} i32 4200, i32 {{[0-9]+}} }, align 8 // CHECK-CONSTEXPR-T2-DAG: @[[FILE_ARG:.*]] = {{.*}}c"ConstexprGlobal.cpp\00" // CHECK-CONSTEXPR-T2-DAG: @[[EMPTY:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +// CHECK-CONSTEXPR-T2-DAG: @[[IMPL_ARG:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE_ARG]], {{[^@]*}}@[[EMPTY]], {{.*}} i32 4400, i32 {{[0-9]+}} }, align 8 // // CHECK-CONSTEXPR-T2: @ConstexprGlobal ={{.*}} global %struct.TestInitConstexpr { -// CHECK-CONSTEXPR-T2-SAME: %struct.source_location { i32 4200, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE_INIT]], {{[^@]*}}@[[FUNC_INIT]], -// CHECK-CONSTEXPR-T2-SAME: {{[^%]*}}%struct.source_location { i32 4400, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE_ARG]], {{[^@]*}}@[[EMPTY]] +// CHECK-CONSTEXPR-T2-SAME: %"class.std::source_location" { %"struct.std::source_location::__impl"* @[[IMPL_INIT]] }, +// CHECK-CONSTEXPR-T2-SAME: %"class.std::source_location" { %"struct.std::source_location::__impl"* @[[IMPL_ARG]] } #line 4400 "ConstexprGlobal.cpp" TestInitConstexpr ConstexprGlobal; extern "C" void test_init_function_constexpr() { // RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-CONSTEXPR-LOCAL // -// CHECK-CONSTEXPR-LOCAL-DAG: @[[FUNC:.*]] = {{.*}}c"test_init_function_constexpr\00" +// CHECK-CONSTEXPR-LOCAL-DAG: @[[FUNC:.*]] = {{.*}}c"void test_init_function_constexpr()\00" // CHECK-CONSTEXPR-LOCAL-DAG: @[[FILE:.*]] = {{.*}}c"ConstexprLocal.cpp\00" +// CHECK-CONSTEXPR-LOCAL-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 4600, i32 {{[0-9]+}} }, align 8 // // CHECK-CONSTEXPR-LOCAL: define{{.*}} void @test_init_function_constexpr() -// CHECK-CONSTEXPR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[TMP:[^,]*]], -// CHECK-CONSTEXPR-LOCAL-SAME: i32 noundef 4600, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] -// CHECK-CONSTEXPR-LOCAL: call void @_ZN17TestInitConstexprC1E15source_location(%struct.TestInitConstexpr* {{[^,]*}} %local_val, {{.*}}%[[TMP]]) +// CHECK-CONSTEXPR-LOCAL-NOT: ret +// CHECK-CONSTEXPR-LOCAL: call %"struct.std::source_location::__impl"* @_ZNSt15source_location7currentEPKv({{.*}} @[[IMPL]] +// CHECK-CONSTEXPR-LOCAL-NOT: ret +// CHECK-CONSTEXPR-LOCAL: call void @_ZN17TestInitConstexprC1ESt15source_location(%struct.TestInitConstexpr* {{[^,]*}} %local_val, %"struct.std::source_location::__impl"* #line 4600 "ConstexprLocal.cpp" TestInitConstexpr local_val; } @@ -180,60 +179,44 @@ struct TestInitAgg { // RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-DEFAULT // // CHECK-AGG-DEFAULT-DAG: @[[FILE:.*]] = {{.*}}c"TestInitAgg.cpp\00" -// CHECK-AGG-DEFAULT-DAG: @[[FUNC:.*]] = {{.*}}c"TestInitAgg\00" +// CHECK-AGG-DEFAULT-DAG: @[[FUNC:.*]] = {{.*}}c"TestInitAgg::TestInitAgg()\00" +// CHECK-AGG-DEFAULT-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 5000, i32 {{[0-9]+}} }, align 8 // // CHECK-AGG-DEFAULT: @GlobalAggDefault ={{.*}} global %struct.TestInitAgg { -// CHECK-AGG-DEFAULT-SAME: %struct.source_location zeroinitializer, -// CHECK-AGG-DEFAULT-SAME: %struct.source_location { i32 5000, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] +// CHECK-AGG-DEFAULT-SAME: %"class.std::source_location" zeroinitializer, +// CHECK-AGG-DEFAULT-SAME: %"class.std::source_location" { %"struct.std::source_location::__impl"* @[[IMPL]] } #line 5400 "GlobalAggDefault.cpp" TestInitAgg GlobalAggDefault; #line 5500 "test_agg_init_test.cpp" extern "C" void test_agg_init() { -// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-BRACE -// -// CHECK-AGG-BRACE-DAG: @[[FILE:.*]] = {{.*}}c"BraceInitEnd.cpp\00" -// CHECK-AGG-BRACE-DAG: @[[FUNC:.*]] = {{.*}}c"test_agg_init\00" -// -// CHECK-AGG-BRACE: define{{.*}} void @test_agg_init() -// CHECK-AGG-BRACE: %[[I2:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_brace_init, i32 0, i32 1 -// CHECK-AGG-BRACE-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[I2]], -// CHECK-AGG-BRACE-SAME: i32 noundef 5700, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] +// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-INIT + +// CHECK-AGG-INIT-DAG: @[[FUNC:.*]] = {{.*}}c"void test_agg_init()\00" + +// CHECK-AGG-INIT-DAG: @[[FILE:.*]] = {{.*}}c"BraceInitEnd.cpp\00" +// CHECK-AGG-INIT-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 5700, i32 {{[0-9]+}} }, align 8 + #line 5600 "BraceInitStart.cpp" TestInitAgg local_brace_init{ #line 5700 "BraceInitEnd.cpp" }; -// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-EQUAL -// -// CHECK-AGG-EQUAL-DAG: @[[FILE:.*]] = {{.*}}c"EqualInitEnd.cpp\00" -// CHECK-AGG-EQUAL-DAG: @[[FUNC:.*]] = {{.*}}c"test_agg_init\00" -// -// CHECK-AGG-EQUAL: define{{.*}} void @test_agg_init() -// CHECK-AGG-EQUAL: %[[I2:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_equal_init, i32 0, i32 1 -// CHECK-AGG-EQUAL-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[I2]], -// CHECK-AGG-EQUAL-SAME: i32 noundef 5900, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] +// CHECK-AGG-INIT-DAG: @[[FILE:.*]] = {{.*}}c"EqualInitEnd.cpp\00" +// CHECK-AGG-INIT-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 5900, i32 {{[0-9]+}} }, align 8 + #line 5800 "EqualInitStart.cpp" TestInitAgg local_equal_init = { #line 5900 "EqualInitEnd.cpp" }; -// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-AGG-LIST -// -// CHECK-AGG-LIST-DAG: @[[FILE_DEFAULT:.*]] = {{.*}}c"InitListEnd.cpp\00" -// CHECK-AGG-LIST-DAG: @[[FILE_ELEM:.*]] = {{.*}}c"ListElem.cpp\00" -// CHECK-AGG-LIST-DAG: @[[FUNC:.*]] = {{.*}}c"test_agg_init\00" -// -// CHECK-AGG-LIST: define{{.*}} void @test_agg_init() -// -// CHECK-AGG-LIST: %[[I1:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_list_init, i32 0, i32 0 -// CHECK-AGG-LIST-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[I1]], -// CHECK-AGG-LIST-SAME: i32 noundef 6100, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE_ELEM]], {{[^@]*}}@[[FUNC]] -// -// CHECK-AGG-LIST: %[[I2:.*]] = getelementptr inbounds %struct.TestInitAgg, %struct.TestInitAgg* %local_list_init, i32 0, i32 1 -// CHECK-AGG-LIST-NEXT: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[I2]], -// CHECK-AGG-LIST-SAME: i32 noundef 6200, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE_DEFAULT]], {{[^@]*}}@[[FUNC]] + +// CHECK-AGG-INIT-DAG: @[[FILE_DEFAULT:.*]] = {{.*}}c"InitListEnd.cpp\00" +// CHECK-AGG-INIT-DAG: @[[FILE_ELEM:.*]] = {{.*}}c"ListElem.cpp\00" +// CHECK-AGG-INIT-DAG: @[[IMPL_DEFAULT:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE_ELEM]], {{[^@]*}}@[[FUNC]], {{.*}} i32 6100, i32 {{[0-9]+}} }, align 8 +// CHECK-AGG-INIT-DAG: @[[IMPL_ELEM:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE_DEFAULT]], {{[^@]*}}@[[FUNC]], {{.*}} i32 6200, i32 {{[0-9]+}} }, align 8 + #line 6000 "InitListStart.cpp" TestInitAgg local_list_init = { @@ -260,14 +243,8 @@ void test_template() { // RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-TEMPL -DINT_ID=1 // // CHECK-TEMPL-DAG: @[[FILE:.*]] = {{.*}}c"local_templ.cpp\00" -// CHECK-TEMPL-DAG: @[[FUNC:.*]] = {{.*}}c"test_template\00" -// -// CHECK-TEMPL: define weak_odr void @_Z13test_templateI15source_locationLi[[INT_ID]]EEvv() -// CHECK-TEMPL-NEXT: entry: -// CHECK-TEMPL-NOT: ret -// -// CHECK-TEMPL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret(%struct.source_location) align 8 %[[TMP:[^,]*]], -// CHECK-TEMPL-SAME: i32 noundef 7300, i32 noundef {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] +// CHECK-TEMPL-DAG: @[[FUNC:.*]] = {{.*}}c"void test_template() [T = std::source_location, V = [[INT_ID]]]\00" +// CHECK-TEMPL-DAG: @[[IMPL:.*]] = private unnamed_addr constant %"struct.std::source_location::__impl" { {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], {{.*}} i32 7300, i32 {{[0-9]+}} }, align 8 #line 7300 "local_templ.cpp" TestTemplate local_templ; } diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp index b312f12..ccb385f 100644 --- a/clang/test/SemaCXX/source_location.cpp +++ b/clang/test/SemaCXX/source_location.cpp @@ -9,37 +9,39 @@ template struct Printer; namespace std { -namespace experimental { -struct source_location { -private: - unsigned int __m_line = 0; - unsigned int __m_col = 0; - const char *__m_file = nullptr; - const char *__m_func = nullptr; +class source_location { + struct __impl; + public: - static constexpr source_location current( - const char *__file = __builtin_FILE(), - const char *__func = __builtin_FUNCTION(), - unsigned int __line = __builtin_LINE(), - unsigned int __col = __builtin_COLUMN()) noexcept { + static constexpr source_location current(const __impl *__p = __builtin_source_location()) noexcept { source_location __loc; - __loc.__m_line = __line; - __loc.__m_col = __col; - __loc.__m_file = __file; - __loc.__m_func = __func; + __loc.__m_impl = __p; return __loc; } constexpr source_location() = default; constexpr source_location(source_location const &) = default; - constexpr unsigned int line() const noexcept { return __m_line; } - constexpr unsigned int column() const noexcept { return __m_col; } - constexpr const char *file() const noexcept { return __m_file; } - constexpr const char *function() const noexcept { return __m_func; } + constexpr unsigned int line() const noexcept { return __m_impl ? __m_impl->_M_line : 0; } + constexpr unsigned int column() const noexcept { return __m_impl ? __m_impl->_M_column : 0; } + constexpr const char *file() const noexcept { return __m_impl ? __m_impl->_M_file_name : ""; } + constexpr const char *function() const noexcept { return __m_impl ? __m_impl->_M_function_name : ""; } + +private: + // Note: The type name "std::source_location::__impl", and its constituent + // field-names are required by __builtin_source_location(). + struct __impl { + const char *_M_file_name; + const char *_M_function_name; + unsigned _M_line; + unsigned _M_column; + }; + const __impl *__m_impl = nullptr; + +public: + using public_impl_alias = __impl; }; -} // namespace experimental } // namespace std -using SL = std::experimental::source_location; +using SL = std::source_location; #include "Inputs/source-location-file.h" namespace SLF = source_location_file; @@ -75,12 +77,14 @@ static_assert(is_same); static_assert(is_same); static_assert(is_same); static_assert(is_same); +static_assert(is_same); // test noexcept static_assert(noexcept(__builtin_LINE())); static_assert(noexcept(__builtin_COLUMN())); static_assert(noexcept(__builtin_FILE())); static_assert(noexcept(__builtin_FUNCTION())); +static_assert(noexcept(__builtin_source_location())); //===----------------------------------------------------------------------===// // __builtin_LINE() @@ -354,7 +358,7 @@ static_assert(test_function()); template constexpr Pair test_func_template(T, U u = U::current()) { - static_assert(is_equal(__func__, U::current().function())); + static_assert(is_equal(__PRETTY_FUNCTION__, U::current().function())); return {u, U::current()}; } template @@ -376,10 +380,11 @@ struct TestCtor { void ctor_tests() { constexpr TestCtor<> Default; constexpr TestCtor<> Template{42}; - static_assert(!is_equal(Default.info.function(), __func__)); - static_assert(is_equal(Default.info.function(), "TestCtor")); - static_assert(is_equal(Template.info.function(), "TestCtor")); - static_assert(is_equal(Template.ctor_info.function(), __func__)); + static const char *XYZZY = Template.info.function(); + static_assert(is_equal(Default.info.function(), "test_func::TestCtor<>::TestCtor() [T = std::source_location]")); + static_assert(is_equal(Default.ctor_info.function(), "")); + static_assert(is_equal(Template.info.function(), "test_func::TestCtor<>::TestCtor(int, U) [T = std::source_location, U = std::source_location]")); + static_assert(is_equal(Template.ctor_info.function(), __PRETTY_FUNCTION__)); } constexpr SL global_sl = SL::current(); @@ -521,7 +526,7 @@ constexpr bool test_in_func() { static_assert(is_equal(b.a.info.file(), "test_func_passed.cpp")); static_assert(is_equal(b.a.func, "test_in_func")); static_assert(is_equal(b.a.func2, "test_in_func")); - static_assert(is_equal(b.a.info.function(), "test_in_func")); + static_assert(is_equal(b.a.info.function(), "bool test_out_of_line_init::test_in_func()")); return true; } static_assert(test_in_func()); diff --git a/clang/test/SemaCXX/source_location_err.cpp b/clang/test/SemaCXX/source_location_err.cpp new file mode 100644 index 0000000..4234216 --- /dev/null +++ b/clang/test/SemaCXX/source_location_err.cpp @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=1 %s +// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=2 %s +// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=3 %s +// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=4 %s +// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify -DTEST=5 %s + +#if TEST == 1 +auto test1a = __builtin_source_location(); // expected-error {{'std::source_location::__impl' was not found}} + +namespace std { +inline namespace NS { + struct source_location; +} +} + +auto test1b = __builtin_source_location(); // expected-error {{'std::source_location::__impl' was not found}} + +namespace std { +inline namespace NS { + struct source_location { + struct __impl; + }; +} +} +auto test1c = __builtin_source_location(); // expected-error {{'std::source_location::__impl' was not found}} + +#elif TEST == 2 +auto test2a = __builtin_source_location(); // expected-error {{'std::source_location::__impl' was not found}} + +namespace std { +inline namespace NS { +struct source_location { + struct __impl { int x; }; +}; +} +} +auto test2b = __builtin_source_location(); // expected-error {{'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'}} + +#elif TEST == 3 +namespace std { +struct source_location { + struct __impl { + int other_member; + char _M_line; + const char *_M_file_name; + char _M_column; + const char *_M_function_name; + }; +}; +} +auto test3 = __builtin_source_location(); // expected-error {{'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'}} + +#elif TEST == 4 +namespace std { +struct source_location { + struct parent {}; + struct __impl : public parent { + char _M_line; + const char *_M_file_name; + char _M_column; + const char *_M_function_name; + }; +}; +} +auto test4 = __builtin_source_location(); // expected-error {{'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'}} + + +#elif TEST == 5 +namespace std { +struct source_location { + struct __impl { + signed char _M_line; // odd integral type to choose, but ok! + const char *_M_file_name; + signed char _M_column; + const char *_M_function_name; + static int other_member; // static members are OK + }; + using BuiltinT = decltype(__builtin_source_location()); // OK. +}; +} + +// Verify that the address cannot be used as a non-type template argument. +template +auto fn1() {return X;} // expected-note {{candidate template ignored: substitution failure: non-type template argument does not refer to any declaration}} +auto test5a = fn1<>(); // expected-error {{no matching function for call to 'fn1'}} + +// (But using integer subobjects by value is okay.) +template _M_column> +auto fn2() {return X;} +auto test5b = fn2<>(); + +// While it's not semantically required, for efficiency, we ensure that two +// source-locations with the same content will point to the same object. Given +// the odd definition of the struct used here (using 'signed char'), any +// line-number modulo 256 will thus have the same content, and be deduplicated. +#line 128 +constexpr auto sl1 = __builtin_source_location(); +#line 384 +constexpr auto sl2 = __builtin_source_location(); +constexpr auto sl3 = __builtin_source_location(); +static_assert(sl1 == sl2); +static_assert(sl1 != sl3); +static_assert(sl1->_M_line == -128); + +#endif diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 681dbba..029adc6 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6478,6 +6478,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::Binding: case Decl::MSProperty: case Decl::MSGuid: + case Decl::UnnamedGlobalConstant: case Decl::TemplateParamObject: case Decl::IndirectField: case Decl::ObjCIvar: -- 2.7.4