diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3316,10 +3316,9 @@ 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**: @@ -3329,6 +3328,7 @@ const char *__builtin_FUNCTION(); unsigned __builtin_LINE(); unsigned __builtin_COLUMN(); // Clang only + const std::source_location::__impl *__builtin_source_location(); **Example of use**: @@ -3355,9 +3355,11 @@ **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 @@ -3368,6 +3370,15 @@ 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 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -119,6 +119,8 @@ C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- 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 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -313,6 +313,10 @@ /// 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; @@ -3060,6 +3064,11 @@ /// 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 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -4188,6 +4188,53 @@ 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 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -4680,16 +4680,17 @@ }; /// 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 @@ 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 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2033,6 +2033,7 @@ 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 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -595,7 +595,7 @@ /// 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 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -41,6 +41,7 @@ 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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11506,4 +11506,9 @@ "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 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -432,6 +432,7 @@ 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 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1134,6 +1134,10 @@ /// 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; @@ -5669,14 +5673,15 @@ 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 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -41,7 +41,7 @@ /// 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 @@ /// 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 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -11841,6 +11841,23 @@ 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 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -6665,6 +6665,7 @@ 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) @@ -6674,8 +6675,8 @@ 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 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -838,6 +838,7 @@ 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 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -3363,6 +3363,31 @@ 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 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2175,26 +2175,11 @@ 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); @@ -2210,6 +2195,8 @@ return "__builtin_LINE"; case Column: return "__builtin_COLUMN"; + case SourceLocStruct: + return "__builtin_source_location"; } llvm_unreachable("unexpected IdentKind!"); } @@ -2246,7 +2233,7 @@ 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("")); @@ -2259,6 +2246,54 @@ : 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 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -465,14 +465,11 @@ 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 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1978,7 +1978,8 @@ 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 @@ // 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 @@ 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)) { @@ -8158,7 +8173,8 @@ 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); @@ -8698,7 +8714,7 @@ 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); @@ -8763,6 +8779,22 @@ 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(); @@ -8780,14 +8812,23 @@ // 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 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -118,6 +118,7 @@ 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 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1909,6 +1909,9 @@ 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.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -406,6 +406,8 @@ llvm::StringMap CFConstantStringMap; llvm::DenseMap ConstantStringMap; + llvm::DenseMap + UnnamedGlobalConstantDeclMap; llvm::DenseMap StaticLocalDeclMap; llvm::DenseMap StaticLocalDeclGuardMap; llvm::DenseMap MaterializedGlobalTemporaryMap; @@ -873,6 +875,10 @@ /// 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/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2894,6 +2894,37 @@ 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/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -791,6 +791,7 @@ /// [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 @@ 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. @@ -2507,6 +2509,7 @@ /// [GNU] '__builtin_FUNCTION' '(' ')' /// [GNU] '__builtin_LINE' '(' ')' /// [CLANG] '__builtin_COLUMN' '(' ')' +/// [GNU] '__builtin_source_location' '(' ')' /// [OCL] '__builtin_astype' '(' assignment-expression ',' type-name ')' /// /// [GNU] offsetof-member-designator: @@ -2729,7 +2732,8 @@ 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; @@ -2746,6 +2750,8 @@ 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 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -199,8 +199,9 @@ 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 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3435,6 +3435,10 @@ 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. @@ -13894,8 +13898,8 @@ return MPTy; } } - } else if (!isa(dcl) && !isa(dcl) && - !isa(dcl) && !isa(dcl)) + } else if (!isa(dcl)) llvm_unreachable("Unknown/unexpected decl type"); } @@ -16079,18 +16083,111 @@ 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 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6995,7 +6995,9 @@ // -- 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 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -868,6 +868,11 @@ 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 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3386,10 +3386,11 @@ /// 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. @@ -11603,8 +11604,8 @@ 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 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -391,6 +391,7 @@ 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 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -370,6 +370,7 @@ 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 @@ 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 @@ 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 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -96,6 +96,7 @@ 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); @@ -967,6 +968,13 @@ 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 --- a/clang/test/CodeGenCXX/builtin-source-location.cpp +++ b/clang/test/CodeGenCXX/builtin-source-location.cpp @@ -10,71 +10,63 @@ #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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 @@ // 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 --- a/clang/test/SemaCXX/source_location.cpp +++ b/clang/test/SemaCXX/source_location.cpp @@ -9,37 +9,39 @@ 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); // 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 @@ 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 @@ 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 @@ 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 --- /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 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6466,6 +6466,7 @@ case Decl::Binding: case Decl::MSProperty: case Decl::MSGuid: + case Decl::UnnamedGlobalConstant: case Decl::TemplateParamObject: case Decl::IndirectField: case Decl::ObjCIvar: