Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -2300,6 +2300,61 @@ token `none`. If a user calls `__builin_suspend`, clang will insert `token none` 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. + +**Syntax**: + +.. code-block:: c + + const char *__builtin_FILE(); + const char *__builtin_FUNCTION(); + unsigned __builtin_LINE(); + unsigned __builtin_COLUMN(); // Clang only + +**Example of use**: + +.. code-block:: c++ + + void my_assert(bool pred, int line = __builtin_LINE(), // Captures line of caller + const char* file = __builtin_FILE(), + const char* function = __builtin_FUNCTION()) { + if (pred) return; + printf("%s:%d assertion failed in function %s\n", file, line, function); + std::abort(); + } + + struct MyAggregateType { + int x; + int line = __builtin_LINE(); // captures line where aggregate initialization occurs + }; + static_assert(MyAggregateType{42}.line == __LINE__); + + struct MyClassType { + int line = __builtin_LINE(); // captures line of the constructor used during initialization + constexpr MyClassType(int) { assert(line == __LINE__); } + }; + +**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. + +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 +default member initializer, the invocation point is the location of the +constructor or aggregate initialization used to create the object. Otherwise +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. + Non-standard C++11 Attributes ============================= Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -271,6 +271,12 @@ llvm::DenseMap MaterializedTemporaryValues; + /// A cache mapping a string value to a StringLiteral object with the same + /// value. + /// + /// This is lazily created. This is intentionally not serialized. + mutable llvm::StringMap StringLiteralCache; + /// Representation of a "canonical" template template parameter that /// is used in canonical template names. class CanonicalTemplateTemplateParm : public llvm::FoldingSetNode { @@ -1323,6 +1329,10 @@ ArrayType::ArraySizeModifier ASM, unsigned IndexTypeQuals) const; + /// Return a type for a constant array for a string literal of the + /// specified element type and length. + QualType getStringLiteralArrayType(QualType EltTy, unsigned Length) const; + /// Returns a vla type where known sizes are replaced with [*]. QualType getVariableArrayDecayedType(QualType Ty) const; @@ -2805,6 +2815,11 @@ APValue *getMaterializedTemporaryValue(const MaterializeTemporaryExpr *E, bool MayCreate); + /// Return a string representing the human readable name for the specified + /// function declaration or file name. Used by SourceLocExpr and + /// PredefinedExpr to cache evaluated results. + StringLiteral *getPredefinedStringLiteralFromCache(StringRef Key) const; + //===--------------------------------------------------------------------===// // Statistics //===--------------------------------------------------------------------===// Index: include/clang/AST/CurrentSourceLocExprScope.h =================================================================== --- include/clang/AST/CurrentSourceLocExprScope.h +++ include/clang/AST/CurrentSourceLocExprScope.h @@ -0,0 +1,75 @@ +//===--- CurrentSourceLocExprScope.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines types used to track the current context needed to evaluate +// a SourceLocExpr. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_CURRENT_SOURCE_LOC_EXPR_SCOPE_H +#define LLVM_CLANG_AST_CURRENT_SOURCE_LOC_EXPR_SCOPE_H + +#include + +namespace clang { +class Expr; + +/// Represents the current source location and context used to determine the +/// value of the source location builtins (ex. __builtin_LINE), including the +/// context of default argument and default initializer expressions. +class CurrentSourceLocExprScope { + /// The CXXDefaultArgExpr or CXXDefaultInitExpr we're currently evaluating. + const Expr *DefaultExpr = nullptr; + +public: + /// A RAII style scope guard used for tracking the current source + /// location and context as used by the source location builtins + /// (ex. __builtin_LINE). + class SourceLocExprScopeGuard; + + const Expr *getDefaultExpr() const { return DefaultExpr; } + + explicit CurrentSourceLocExprScope() = default; + +private: + explicit CurrentSourceLocExprScope(const Expr *DefaultExpr) + : DefaultExpr(DefaultExpr) {} + + CurrentSourceLocExprScope(CurrentSourceLocExprScope const &) = default; + CurrentSourceLocExprScope & + operator=(CurrentSourceLocExprScope const &) = default; +}; + +class CurrentSourceLocExprScope::SourceLocExprScopeGuard { +public: + SourceLocExprScopeGuard(const Expr *DefaultExpr, + CurrentSourceLocExprScope &Current) + : Current(Current), OldVal(Current), Enable(false) { + assert(DefaultExpr && "the new scope should not be empty"); + if ((Enable = (Current.getDefaultExpr() == nullptr))) + Current = CurrentSourceLocExprScope(DefaultExpr); + } + + ~SourceLocExprScopeGuard() { + if (Enable) + Current = OldVal; + } + +private: + SourceLocExprScopeGuard(SourceLocExprScopeGuard const &) = delete; + SourceLocExprScopeGuard &operator=(SourceLocExprScopeGuard const &) = delete; + + CurrentSourceLocExprScope &Current; + CurrentSourceLocExprScope OldVal; + bool Enable; +}; + +} // end namespace clang + +#endif // LLVM_CLANG_AST_CURRENT_SOURCE_LOC_EXPR_SCOPE_H Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -4171,6 +4171,70 @@ } }; +/// Represents a function call to one of __builtin_LINE(), __builtin_COLUMN(), +/// __builtin_FUNCTION(), or __builtin_FILE(). +class SourceLocExpr final : public Expr { + SourceLocation BuiltinLoc, RParenLoc; + DeclContext *ParentContext; + +public: + enum IdentKind { Function, File, Line, Column }; + + SourceLocExpr(const ASTContext &Ctx, IdentKind Type, SourceLocation BLoc, + SourceLocation RParenLoc, DeclContext *Context); + + /// Build an empty call expression. + explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {} + + /// Return the result of evaluating this SourceLocExpr in the specified + /// (and possibly null) default argument or initialization context. + APValue EvaluateInContext(const ASTContext &Ctx, + const Expr *DefaultExpr) const; + + /// Return a string representing the name of the specific builtin function. + StringRef getBuiltinStr() const; + + IdentKind getIdentKind() const { + return static_cast(SourceLocExprBits.Kind); + } + + bool isStringType() const { + switch (getIdentKind()) { + case File: + case Function: + return true; + case Line: + case Column: + return false; + } + } + bool isIntType() const LLVM_READONLY { return !isStringType(); } + + /// If the SourceLocExpr has been resolved return the subexpression + /// representing the resolved value. Otherwise return null. + const DeclContext *getParentContext() const { return ParentContext; } + DeclContext *getParentContext() { return ParentContext; } + + SourceLocation getLocation() const { return BuiltinLoc; } + SourceLocation getBeginLoc() const { return BuiltinLoc; } + SourceLocation getEndLoc() const { return RParenLoc; } + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + const_child_range children() const { + return const_child_range(child_iterator(), child_iterator()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == SourceLocExprClass; + } + +private: + friend class ASTStmtReader; +}; + /// Describes an C or C++ initializer list. /// /// InitListExpr describes an initializer list, which can be used to Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -1121,7 +1121,11 @@ /// The parameter whose default is being used. ParmVarDecl *Param; - CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param) + /// The context where the default argument expression was used. + DeclContext *UsedContext; + + CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param, + DeclContext *UsedContext) : Expr(SC, Param->hasUnparsedDefaultArg() ? Param->getType().getNonReferenceType() @@ -1129,7 +1133,7 @@ Param->getDefaultArg()->getValueKind(), Param->getDefaultArg()->getObjectKind(), false, false, false, false), - Param(Param) { + Param(Param), UsedContext(UsedContext) { CXXDefaultArgExprBits.Loc = Loc; } @@ -1139,8 +1143,10 @@ // \p Param is the parameter whose default argument is used by this // expression. static CXXDefaultArgExpr *Create(const ASTContext &C, SourceLocation Loc, - ParmVarDecl *Param) { - return new (C) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param); + ParmVarDecl *Param, + DeclContext *UsedContext) { + return new (C) + CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, UsedContext); } // Retrieve the parameter that the argument was created from. @@ -1151,6 +1157,9 @@ const Expr *getExpr() const { return getParam()->getDefaultArg(); } Expr *getExpr() { return getParam()->getDefaultArg(); } + const DeclContext *getUsedContext() const { return UsedContext; } + DeclContext *getUsedContext() { return UsedContext; } + /// Retrieve the location where this default argument was actually used. SourceLocation getUsedLocation() const { return CXXDefaultArgExprBits.Loc; } @@ -1190,8 +1199,11 @@ /// The field whose default is being used. FieldDecl *Field; + /// The context where the default initializer expression was used. + DeclContext *UsedContext; + CXXDefaultInitExpr(const ASTContext &Ctx, SourceLocation Loc, - FieldDecl *Field, QualType Ty); + FieldDecl *Field, QualType Ty, DeclContext *UsedContext); CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {} @@ -1199,8 +1211,8 @@ /// \p Field is the non-static data member whose default initializer is used /// by this expression. static CXXDefaultInitExpr *Create(const ASTContext &Ctx, SourceLocation Loc, - FieldDecl *Field) { - return new (Ctx) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType()); + FieldDecl *Field, DeclContext *UsedContext) { + return new (Ctx) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType(), UsedContext); } /// Get the field whose initializer will be used. @@ -1217,6 +1229,13 @@ return Field->getInClassInitializer(); } + const DeclContext *getUsedContext() const { return UsedContext; } + DeclContext *getUsedContext() { return UsedContext; } + + /// Retrieve the location where this default initializer expression was + /// actually used. + SourceLocation getUsedLocation() const { return getBeginLoc(); } + SourceLocation getBeginLoc() const { return CXXDefaultInitExprBits.Loc; } SourceLocation getEndLoc() const { return CXXDefaultInitExprBits.Loc; } Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2550,6 +2550,8 @@ DEF_TRAVERSE_STMT(ShuffleVectorExpr, {}) DEF_TRAVERSE_STMT(ConvertVectorExpr, {}) DEF_TRAVERSE_STMT(StmtExpr, {}) +DEF_TRAVERSE_STMT(SourceLocExpr, {}) + DEF_TRAVERSE_STMT(UnresolvedLookupExpr, { TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc())); if (S->hasExplicitTemplateArgs()) { Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -550,6 +550,17 @@ unsigned ResultIndex : 32 - 8 - NumExprBits; }; + class SourceLocExprBitfields { + friend class ASTStmtReader; + friend class SourceLocExpr; + + unsigned : NumExprBits; + + /// The kind of source location builtin represented by the SourceLocExpr. + /// Ex. __builtin_LINE, __builtin_FUNCTION, ect. + unsigned Kind : 2; + }; + //===--- C++ Expression bitfields classes ---===// class CXXOperatorCallExprBitfields { @@ -935,6 +946,7 @@ ParenListExprBitfields ParenListExprBits; GenericSelectionExprBitfields GenericSelectionExprBits; PseudoObjectExprBitfields PseudoObjectExprBits; + SourceLocExprBitfields SourceLocExprBits; // C++ Expressions CXXOperatorCallExprBitfields CXXOperatorCallExprBits; Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -96,6 +96,7 @@ def VAArgExpr : DStmt; def GenericSelectionExpr : DStmt; def PseudoObjectExpr : DStmt; +def SourceLocExpr : DStmt; // Wrapper expressions def FullExpr : DStmt; Index: include/clang/Basic/TokenKinds.def =================================================================== --- include/clang/Basic/TokenKinds.def +++ include/clang/Basic/TokenKinds.def @@ -404,6 +404,11 @@ KEYWORD(__attribute , KEYALL) KEYWORD(__builtin_choose_expr , KEYALL) KEYWORD(__builtin_offsetof , KEYALL) +KEYWORD(__builtin_FILE , KEYALL) +KEYWORD(__builtin_FUNCTION , KEYALL) +KEYWORD(__builtin_LINE , KEYALL) +KEYWORD(__builtin_COLUMN , KEYALL) + // __builtin_types_compatible_p is a GNU C extension that we handle like a C++ // type trait. TYPE_TRAIT_2(__builtin_types_compatible_p, TypeCompatible, KEYNOCXX) Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4612,6 +4612,21 @@ ExprResult BuildVAArgExpr(SourceLocation BuiltinLoc, Expr *E, TypeSourceInfo *TInfo, SourceLocation RPLoc); + // __builtin_LINE(), __builtin_FUNCTION(), __builtin_FILE(), + // __builtin_COLUMN() + ExprResult ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind, + SourceLocation BuiltinLoc, + SourceLocation RPLoc); + + /// Build a potentially resolved SourceLocExpr. + /// + /// \param SubExpr - null when the SourceLocExpr is unresolved, otherwise + /// SubExpr will be a literal expression representing the value of the + /// builtin call. + ExprResult BuildSourceLocExpr(SourceLocExpr::IdentKind Kind, + SourceLocation BuiltinLoc, SourceLocation RPLoc, + DeclContext *ParentContext); + // __null ExprResult ActOnGNUNullExpr(SourceLocation TokenLoc); Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1732,6 +1732,9 @@ /// A GNUNullExpr record. EXPR_GNU_NULL, + /// A SourceLocExpr record. + EXPR_SOURCE_LOC, + /// A ShuffleVectorExpr record. EXPR_SHUFFLE_VECTOR, Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -10208,6 +10208,31 @@ return MaterializedTemporaryValues.lookup(E); } +QualType ASTContext::getStringLiteralArrayType(QualType EltTy, + unsigned Length) const { + // A C++ string literal has a const-qualified element type (C++ 2.13.4p1). + if (getLangOpts().CPlusPlus || getLangOpts().ConstStrings) + EltTy = EltTy.withConst(); + + EltTy = adjustStringLiteralBaseType(EltTy); + + // Get an array type for the string, according to C99 6.4.5. This includes + // the null terminator character. + return getConstantArrayType(EltTy, llvm::APInt(32, Length + 1), + ArrayType::Normal, /*IndexTypeQuals*/ 0); +} + +StringLiteral * +ASTContext::getPredefinedStringLiteralFromCache(StringRef Key) const { + StringLiteral *&Result = StringLiteralCache[Key]; + if (!Result) + Result = StringLiteral::Create( + *this, Key, StringLiteral::Ascii, + /*Pascal*/ false, getStringLiteralArrayType(CharTy, Key.size()), + SourceLocation()); + return Result; +} + bool ASTContext::AtomicUsesUnsupportedLibcall(const AtomicExpr *E) const { const llvm::Triple &T = getTargetInfo().getTriple(); if (!T.isOSDarwin()) Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -6801,8 +6801,12 @@ if (!ToParamOrErr) return ToParamOrErr.takeError(); + auto UsedContextOrErr = Importer.ImportContext(E->getUsedContext()); + if (!UsedContextOrErr) + return UsedContextOrErr.takeError(); + return CXXDefaultArgExpr::Create( - Importer.getToContext(), *ToUsedLocOrErr, *ToParamOrErr); + Importer.getToContext(), *ToUsedLocOrErr, *ToParamOrErr, *UsedContextOrErr); } ExpectedStmt @@ -7525,8 +7529,12 @@ if (!ToFieldOrErr) return ToFieldOrErr.takeError(); + auto UsedContextOrErr = Importer.ImportContext(E->getUsedContext()); + if (!UsedContextOrErr) + return UsedContextOrErr.takeError(); + return CXXDefaultInitExpr::Create( - Importer.getToContext(), *ToBeginLocOrErr, *ToFieldOrErr); + Importer.getToContext(), *ToBeginLocOrErr, *ToFieldOrErr, *UsedContextOrErr); } ExpectedStmt ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) { Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -10,13 +10,14 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/Expr.h" +#include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" -#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" @@ -1992,6 +1993,91 @@ 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, + DeclContext *ParentContext) + : Expr(SourceLocExprClass, getDecayedSourceLocExprType(Ctx, Kind), + VK_RValue, OK_Ordinary, false, false, false, false), + BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext) { + SourceLocExprBits.Kind = Kind; +} + +StringRef SourceLocExpr::getBuiltinStr() const { + switch (getIdentKind()) { + case File: + return "__builtin_FILE"; + case Function: + return "__builtin_FUNCTION"; + case Line: + return "__builtin_LINE"; + case Column: + return "__builtin_COLUMN"; + } + llvm_unreachable("unexpected IdentKind!"); +} + +APValue SourceLocExpr::EvaluateInContext(const ASTContext &Ctx, + const Expr *DefaultExpr) const { + SourceLocation Loc; + const DeclContext *Context; + + std::tie(Loc, + Context) = [&]() -> std::pair { + if (auto *DIE = dyn_cast_or_null(DefaultExpr)) + return {DIE->getUsedLocation(), DIE->getUsedContext()}; + if (auto *DAE = dyn_cast_or_null(DefaultExpr)) + return {DAE->getUsedLocation(), DAE->getUsedContext()}; + return {this->getLocation(), this->getParentContext()}; + }(); + + PresumedLoc PLoc = Ctx.getSourceManager().getPresumedLoc( + Ctx.getSourceManager().getExpansionRange(Loc).getEnd()); + + auto MakeStringLiteral = [&](StringRef Tmp) { + using LValuePathEntry = APValue::LValuePathEntry; + StringLiteral *Res = Ctx.getPredefinedStringLiteralFromCache(Tmp); + // Decay the string to a pointer to the first character. + LValuePathEntry Path[1] = {LValuePathEntry::ArrayIndex(0)}; + return APValue(Res, CharUnits::Zero(), Path, /*OnePastTheEnd=*/false); + }; + + switch (getIdentKind()) { + case SourceLocExpr::File: + return MakeStringLiteral(PLoc.getFilename()); + case SourceLocExpr::Function: { + const Decl *CurDecl = dyn_cast_or_null(Context); + return MakeStringLiteral( + CurDecl ? PredefinedExpr::ComputeName(PredefinedExpr::Function, CurDecl) + : std::string("")); + } + case SourceLocExpr::Line: + case SourceLocExpr::Column: { + llvm::APSInt IntVal(Ctx.getIntWidth(Ctx.UnsignedIntTy), + /*IsUnsigned=*/true); + IntVal = getIdentKind() == SourceLocExpr::Line ? PLoc.getLine() + : PLoc.getColumn(); + return APValue(IntVal); + } + } + llvm_unreachable("unhandled case"); +} + InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc, ArrayRef initExprs, SourceLocation rbraceloc) : Expr(InitListExprClass, QualType(), VK_RValue, OK_Ordinary, false, false, @@ -3156,6 +3242,7 @@ case ObjCAvailabilityCheckExprClass: case CXXUuidofExprClass: case OpaqueValueExprClass: + case SourceLocExprClass: // These never have a side-effect. return false; Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -907,13 +907,14 @@ } CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &Ctx, SourceLocation Loc, - FieldDecl *Field, QualType Ty) + FieldDecl *Field, QualType Ty, + DeclContext *UsedContext) : Expr(CXXDefaultInitExprClass, Ty.getNonLValueExprType(Ctx), Ty->isLValueReferenceType() ? VK_LValue : Ty->isRValueReferenceType() ? VK_XValue : VK_RValue, /*FIXME*/ OK_Ordinary, false, false, false, false), - Field(Field) { + Field(Field), UsedContext(UsedContext) { CXXDefaultInitExprBits.Loc = Loc; assert(Field->hasInClassInitializer()); } Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -191,6 +191,7 @@ case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: + case Expr::SourceLocExprClass: return Cl::CL_PRValue; case Expr::ConstantExprClass: Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -37,6 +37,7 @@ #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/CurrentSourceLocExprScope.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Expr.h" #include "clang/AST/OSLog.h" @@ -64,6 +65,9 @@ struct CallStackFrame; struct EvalInfo; + using SourceLocExprScopeGuard = + CurrentSourceLocExprScope::SourceLocExprScopeGuard; + static QualType getType(APValue::LValueBase B) { if (!B) return QualType(); if (const ValueDecl *D = B.dyn_cast()) { @@ -106,12 +110,12 @@ /// Get an LValue path entry, which is known to not be an array index, as a /// field declaration. static const FieldDecl *getAsField(APValue::LValuePathEntry E) { - return dyn_cast(E.getAsBaseOrMember().getPointer()); + return dyn_cast_or_null(E.getAsBaseOrMember().getPointer()); } /// Get an LValue path entry, which is known to not be an array index, as a /// base class declaration. static const CXXRecordDecl *getAsBaseClass(APValue::LValuePathEntry E) { - return dyn_cast(E.getAsBaseOrMember().getPointer()); + return dyn_cast_or_null(E.getAsBaseOrMember().getPointer()); } /// Determine whether this LValue path entry for a base class names a virtual /// base class. @@ -464,6 +468,10 @@ /// parameters' function scope indices. APValue *Arguments; + /// Source location information about the default argument or default + /// initializer expression we're evaluating, if any. + CurrentSourceLocExprScope CurSourceLocExprScope; + // Note that we intentionally use std::map here so that references to // values are stable. typedef std::pair MapKeyTy; @@ -2732,6 +2740,9 @@ /// Extract the value of a character from a string literal. static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, uint64_t Index) { + assert(!isa(Lit) && + "SourceLocExpr should have already been converted to a StringLiteral"); + // FIXME: Support MakeStringConstant if (const auto *ObjCEnc = dyn_cast(Lit)) { std::string Str; @@ -3469,6 +3480,7 @@ // Check for special cases where there is no existing APValue to look at. const Expr *Base = LVal.Base.dyn_cast(); + if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) { if (const CompoundLiteralExpr *CLE = dyn_cast(Base)) { // In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the @@ -5242,6 +5254,7 @@ { return StmtVisitorTy::Visit(E->getReplacement()); } bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { TempVersionRAII RAII(*Info.CurrentCall); + SourceLocExprScopeGuard Guard(E, Info.CurrentCall->CurSourceLocExprScope); return StmtVisitorTy::Visit(E->getExpr()); } bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) { @@ -5249,8 +5262,10 @@ // The initializer may not have been parsed yet, or might be erroneous. if (!E->getExpr()) return Error(E); + SourceLocExprScopeGuard Guard(E, Info.CurrentCall->CurSourceLocExprScope); return StmtVisitorTy::Visit(E->getExpr()); } + // We cannot create any objects for which cleanups are required, so there is // nothing to do here; all cleanups must come from unevaluated subexpressions. bool VisitExprWithCleanups(const ExprWithCleanups *E) @@ -6327,6 +6342,14 @@ return true; } + bool VisitSourceLocExpr(const SourceLocExpr *E) { + assert(E->isStringType() && "SourceLocExpr isn't a pointer type?"); + APValue LValResult = E->EvaluateInContext( + Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getDefaultExpr()); + Result.setFrom(Info.Ctx, LValResult); + return true; + } + // FIXME: Missing: @protocol, @selector }; } // end anonymous namespace @@ -7982,7 +8005,7 @@ bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E); bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); - + bool VisitSourceLocExpr(const SourceLocExpr *E); // FIXME: Missing: array subscript of vector, member of vector }; @@ -8058,6 +8081,12 @@ return true; } +bool IntExprEvaluator::VisitSourceLocExpr(const SourceLocExpr *E) { + APValue Evaluated = E->EvaluateInContext( + Info.Ctx, Info.CurrentCall->CurSourceLocExprScope.getDefaultExpr()); + return Success(Evaluated, E); +} + static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result, EvalInfo &Info) { if (E->getType()->isFixedPointType()) { @@ -11899,7 +11928,7 @@ case Expr::SizeOfPackExprClass: case Expr::GNUNullExprClass: - // GCC considers the GNU __null value to be an integral constant expression. + case Expr::SourceLocExprClass: return NoDiag(); case Expr::SubstNonTypeTemplateParmExprClass: Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3589,6 +3589,7 @@ case Expr::AsTypeExprClass: case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: + case Expr::SourceLocExprClass: case Expr::FixedPointLiteralClass: { if (!NullOut) { Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -905,6 +905,10 @@ // Expr printing methods. //===----------------------------------------------------------------------===// +void StmtPrinter::VisitSourceLocExpr(SourceLocExpr *Node) { + OS << Node->getBuiltinStr() << "()"; +} + void StmtPrinter::VisitConstantExpr(ConstantExpr *Node) { PrintExpr(Node->getSubExpr()); } Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1882,6 +1882,10 @@ VisitExpr(E); } +void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) { + VisitExpr(E); +} + void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -1315,11 +1315,15 @@ return LV; } - case Expr::CXXDefaultArgExprClass: - return EmitLValue(cast(E)->getExpr()); + case Expr::CXXDefaultArgExprClass: { + auto *DAE = cast(E); + CXXDefaultArgExprScope Scope(*this, DAE); + return EmitLValue(DAE->getExpr()); + } case Expr::CXXDefaultInitExprClass: { - CXXDefaultInitExprScope Scope(*this); - return EmitLValue(cast(E)->getExpr()); + auto *DIE = cast(E); + CXXDefaultInitExprScope Scope(*this, DIE); + return EmitLValue(DIE->getExpr()); } case Expr::CXXTypeidExprClass: return EmitCXXTypeidLValue(cast(E)); Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -165,10 +165,11 @@ void VisitImplicitValueInitExpr(ImplicitValueInitExpr *E); void VisitNoInitExpr(NoInitExpr *E) { } // Do nothing. void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { + CodeGenFunction::CXXDefaultArgExprScope Scope(CGF, DAE); Visit(DAE->getExpr()); } void VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { - CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF, DIE); Visit(DIE->getExpr()); } void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E); Index: lib/CodeGen/CGExprComplex.cpp =================================================================== --- lib/CodeGen/CGExprComplex.cpp +++ lib/CodeGen/CGExprComplex.cpp @@ -213,10 +213,11 @@ return Visit(E->getSubExpr()); } ComplexPairTy VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { + CodeGenFunction::CXXDefaultArgExprScope Scope(CGF, DAE); return Visit(DAE->getExpr()); } ComplexPairTy VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { - CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF, DIE); return Visit(DIE->getExpr()); } ComplexPairTy VisitExprWithCleanups(ExprWithCleanups *E) { Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -884,10 +884,6 @@ llvm_unreachable("Invalid CastKind"); } - llvm::Constant *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE, QualType T) { - return Visit(DAE->getExpr(), T); - } - llvm::Constant *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE, QualType T) { // No need for a DefaultInitExprScope: we don't handle 'this' in a // constant expression. Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -16,6 +16,7 @@ #include "CGObjCRuntime.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" +#include "ConstantEmitter.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" @@ -640,12 +641,20 @@ Value *VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) { return EmitLoadOfLValue(E); } + Value *VisitSourceLocExpr(SourceLocExpr *SLE) { + auto &Ctx = CGF.getContext(); + APValue Evaluated = + SLE->EvaluateInContext(Ctx, CGF.CurSourceLocExprScope.getDefaultExpr()); + return ConstantEmitter(CGF.CGM, &CGF) + .emitAbstract(SLE->getLocation(), Evaluated, SLE->getType()); + } Value *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { + CodeGenFunction::CXXDefaultArgExprScope Scope(CGF, DAE); return Visit(DAE->getExpr()); } Value *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) { - CodeGenFunction::CXXDefaultInitExprScope Scope(CGF); + CodeGenFunction::CXXDefaultInitExprScope Scope(CGF, DIE); return Visit(DIE->getExpr()); } Value *VisitCXXThisExpr(CXXThisExpr *TE) { Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -22,6 +22,7 @@ #include "EHScopeStack.h" #include "VarBypassDetector.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/CurrentSourceLocExprScope.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" @@ -1393,6 +1394,12 @@ SourceLocation LastStopPoint; public: + /// Source location information about the default argument or member + /// initializer expression we're evaluating, if any. + CurrentSourceLocExprScope CurSourceLocExprScope; + using SourceLocExprScopeGuard = + CurrentSourceLocExprScope::SourceLocExprScopeGuard; + /// A scope within which we are constructing the fields of an object which /// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use /// if we need to evaluate a CXXDefaultInitExpr within the evaluation. @@ -1413,11 +1420,12 @@ /// The scope of a CXXDefaultInitExpr. Within this scope, the value of 'this' /// is overridden to be the object under construction. - class CXXDefaultInitExprScope { + class CXXDefaultInitExprScope { public: - CXXDefaultInitExprScope(CodeGenFunction &CGF) - : CGF(CGF), OldCXXThisValue(CGF.CXXThisValue), - OldCXXThisAlignment(CGF.CXXThisAlignment) { + CXXDefaultInitExprScope(CodeGenFunction &CGF, const CXXDefaultInitExpr *E) + : CGF(CGF), OldCXXThisValue(CGF.CXXThisValue), + OldCXXThisAlignment(CGF.CXXThisAlignment), + SourceLocScope(E, CGF.CurSourceLocExprScope) { CGF.CXXThisValue = CGF.CXXDefaultInitExprThis.getPointer(); CGF.CXXThisAlignment = CGF.CXXDefaultInitExprThis.getAlignment(); } @@ -1430,6 +1438,12 @@ CodeGenFunction &CGF; llvm::Value *OldCXXThisValue; CharUnits OldCXXThisAlignment; + SourceLocExprScopeGuard SourceLocScope; + }; + + struct CXXDefaultArgExprScope : SourceLocExprScopeGuard { + CXXDefaultArgExprScope(CodeGenFunction &CGF, const CXXDefaultArgExpr *E) + : SourceLocExprScopeGuard(E, CGF.CurSourceLocExprScope) {} }; /// The scope of an ArrayInitLoopExpr. Within this scope, the value of the Index: lib/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -639,6 +639,10 @@ /// [GNU] '__builtin_offsetof' '(' type-name ',' offsetof-member-designator')' /// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' /// assign-expr ')' +/// [GNU] '__builtin_FILE' '(' ')' +/// [GNU] '__builtin_FUNCTION' '(' ')' +/// [GNU] '__builtin_LINE' '(' ')' +/// [CLANG] '__builtin_COLUMN' '(' ')' /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' /// [GNU] '__null' /// [OBJC] '[' objc-message-expr ']' @@ -1106,6 +1110,10 @@ case tok::kw___builtin_choose_expr: case tok::kw___builtin_astype: // primary-expression: [OCL] as_type() case tok::kw___builtin_convertvector: + case tok::kw___builtin_COLUMN: + case tok::kw___builtin_FILE: + case tok::kw___builtin_FUNCTION: + case tok::kw___builtin_LINE: return ParseBuiltinPrimaryExpression(); case tok::kw___null: return Actions.ActOnGNUNullExpr(ConsumeToken()); @@ -2067,6 +2075,10 @@ /// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' /// assign-expr ')' /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' +/// [GNU] '__builtin_FILE' '(' ')' +/// [GNU] '__builtin_FUNCTION' '(' ')' +/// [GNU] '__builtin_LINE' '(' ')' +/// [CLANG] '__builtin_COLUMN' '(' ')' /// [OCL] '__builtin_astype' '(' assignment-expression ',' type-name ')' /// /// [GNU] offsetof-member-designator: @@ -2286,6 +2298,33 @@ ConsumeParen()); break; } + case tok::kw___builtin_COLUMN: + case tok::kw___builtin_FILE: + case tok::kw___builtin_FUNCTION: + case tok::kw___builtin_LINE: { + // Attempt to consume the r-paren. + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected) << tok::r_paren; + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + SourceLocExpr::IdentKind Kind = [&] { + switch (T) { + case tok::kw___builtin_FILE: + return SourceLocExpr::File; + case tok::kw___builtin_FUNCTION: + return SourceLocExpr::Function; + case tok::kw___builtin_LINE: + return SourceLocExpr::Line; + case tok::kw___builtin_COLUMN: + return SourceLocExpr::Column; + default: + llvm_unreachable("invalid keyword"); + } + }(); + Res = Actions.ActOnSourceLocExpr(Kind, StartLoc, ConsumeParen()); + break; + } } if (Res.isInvalid()) Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -62,7 +62,7 @@ public: CheckDefaultArgumentVisitor(Expr *defarg, Sema *s) - : DefaultArg(defarg), S(s) {} + : DefaultArg(defarg), S(s) {} bool VisitExpr(Expr *Node); bool VisitDeclRefExpr(DeclRefExpr *DRE); @@ -13025,7 +13025,7 @@ // If we already have the in-class initializer nothing needs to be done. if (Field->getInClassInitializer()) - return CXXDefaultInitExpr::Create(Context, Loc, Field); + return CXXDefaultInitExpr::Create(Context, Loc, Field, CurContext); // If we might have already tried and failed to instantiate, don't try again. if (Field->isInvalidDecl()) @@ -13066,7 +13066,7 @@ Field->setInvalidDecl(); return ExprError(); } - return CXXDefaultInitExpr::Create(Context, Loc, Field); + return CXXDefaultInitExpr::Create(Context, Loc, Field, CurContext); } // DR1351: Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1289,6 +1289,7 @@ case Expr::PredefinedExprClass: case Expr::SizeOfPackExprClass: case Expr::StringLiteralClass: + case Expr::SourceLocExprClass: // These expressions can never throw. return CT_Cannot; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -1683,20 +1683,8 @@ Diag(RemovalDiagLoc, RemovalDiag); } - - QualType CharTyConst = CharTy; - // A C++ string literal has a const-qualified element type (C++ 2.13.4p1). - if (getLangOpts().CPlusPlus || getLangOpts().ConstStrings) - CharTyConst.addConst(); - - CharTyConst = Context.adjustStringLiteralBaseType(CharTyConst); - - // Get an array type for the string, according to C99 6.4.5. This includes - // the nul terminator character as well as the string length for pascal - // strings. - QualType StrTy = Context.getConstantArrayType( - CharTyConst, llvm::APInt(32, Literal.GetNumStringChars() + 1), - ArrayType::Normal, 0); + QualType StrTy = + Context.getStringLiteralArrayType(CharTy, Literal.GetNumStringChars()); // Pass &StringTokLocs[0], StringTokLocs.size() to factory! StringLiteral *Lit = StringLiteral::Create(Context, Literal.GetString(), @@ -4873,7 +4861,7 @@ FunctionDecl *FD, ParmVarDecl *Param) { if (CheckCXXDefaultArgExpr(CallLoc, FD, Param)) return ExprError(); - return CXXDefaultArgExpr::Create(Context, CallLoc, Param); + return CXXDefaultArgExpr::Create(Context, CallLoc, Param, CurContext); } Sema::VariadicCallType @@ -5138,8 +5126,7 @@ } else { assert(Param && "can't use default arguments without a known callee"); - ExprResult ArgExpr = - BuildCXXDefaultArgExpr(CallLoc, FDecl, Param); + ExprResult ArgExpr = BuildCXXDefaultArgExpr(CallLoc, FDecl, Param); if (ArgExpr.isInvalid()) return true; @@ -14097,6 +14084,20 @@ return new (Context) GNUNullExpr(Ty, TokenLoc); } +ExprResult Sema::ActOnSourceLocExpr(SourceLocExpr::IdentKind Kind, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + return BuildSourceLocExpr(Kind, BuiltinLoc, RPLoc, CurContext); +} + +ExprResult Sema::BuildSourceLocExpr(SourceLocExpr::IdentKind Kind, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, + DeclContext *ParentContext) { + return new (Context) + SourceLocExpr(Context, Kind, BuiltinLoc, RPLoc, ParentContext); +} + bool Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp, bool Diagnose) { if (!getLangOpts().ObjC) Index: lib/Sema/SemaExprObjC.cpp =================================================================== --- lib/Sema/SemaExprObjC.cpp +++ lib/Sema/SemaExprObjC.cpp @@ -1097,12 +1097,7 @@ // The type of @encode is the same as the type of the corresponding string, // which is an array type. - StrTy = Context.CharTy; - // A C++ string literal has a const-qualified element type (C++ 2.13.4p1). - if (getLangOpts().CPlusPlus || getLangOpts().ConstStrings) - StrTy.addConst(); - StrTy = Context.getConstantArrayType(StrTy, llvm::APInt(32, Str.size()+1), - ArrayType::Normal, 0); + StrTy = Context.getStringLiteralArrayType(Context.CharTy, Str.size()); } return new (Context) ObjCEncodeExpr(StrTy, EncodedTypeInfo, AtLoc, RParenLoc); Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -2713,9 +2713,9 @@ /// By default, builds a new default-argument expression, which does not /// require any semantic analysis. Subclasses may override this routine to /// provide different behavior. - ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, - ParmVarDecl *Param) { - return CXXDefaultArgExpr::Create(getSema().Context, Loc, Param); + ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, ParmVarDecl *Param) { + return CXXDefaultArgExpr::Create(getSema().Context, Loc, Param, + getSema().CurContext); } /// Build a new C++11 default-initialization expression. @@ -2725,7 +2725,8 @@ /// routine to provide different behavior. ExprResult RebuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { - return CXXDefaultInitExpr::Create(getSema().Context, Loc, Field); + return CXXDefaultInitExpr::Create(getSema().Context, Loc, Field, + getSema().CurContext); } /// Build a new C++ zero-initialization expression. @@ -2979,6 +2980,18 @@ RParenLoc, Length, PartialArgs); } + /// Build a new expression representing a call to a source location + /// builtin. + /// + /// 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, + SourceLocation RPLoc, + DeclContext *ParentContext) { + return getSema().BuildSourceLocExpr(Kind, BuiltinLoc, RPLoc, ParentContext); + } + /// Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. @@ -10132,6 +10145,19 @@ return getDerived().TransformCallExpr(E); } +template +ExprResult TreeTransform::TransformSourceLocExpr(SourceLocExpr *E) { + bool NeedRebuildFunc = E->getIdentKind() == SourceLocExpr::Function && + getSema().CurContext != E->getParentContext(); + + if (!getDerived().AlwaysRebuild() && !NeedRebuildFunc) + return E; + + return getDerived().RebuildSourceLocExpr(E->getIdentKind(), E->getBeginLoc(), + E->getEndLoc(), + getSema().CurContext); +} + template ExprResult TreeTransform::TransformCUDAKernelCallExpr(CUDAKernelCallExpr *E) { @@ -10358,8 +10384,8 @@ if (!Param) return ExprError(); - if (!getDerived().AlwaysRebuild() && - Param == E->getParam()) + if (!getDerived().AlwaysRebuild() && Param == E->getParam() && + E->getUsedContext() == SemaRef.CurContext) return E; return getDerived().RebuildCXXDefaultArgExpr(E->getUsedLocation(), Param); @@ -10373,7 +10399,8 @@ if (!Field) return ExprError(); - if (!getDerived().AlwaysRebuild() && Field == E->getField()) + if (!getDerived().AlwaysRebuild() && Field == E->getField() && + E->getUsedContext() == SemaRef.CurContext) return E; return getDerived().RebuildCXXDefaultInitExpr(E->getExprLoc(), Field); Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -968,6 +968,15 @@ E->setIsMicrosoftABI(Record.readInt()); } +void ASTStmtReader::VisitSourceLocExpr(SourceLocExpr *E) { + VisitExpr(E); + E->ParentContext = ReadDeclAs(); + E->BuiltinLoc = ReadSourceLocation(); + E->RParenLoc = ReadSourceLocation(); + E->SourceLocExprBits.Kind = + static_cast(Record.readInt()); +} + void ASTStmtReader::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); E->setAmpAmpLoc(ReadSourceLocation()); @@ -1487,12 +1496,14 @@ void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { VisitExpr(E); E->Param = ReadDeclAs(); + E->UsedContext = ReadDeclAs(); E->CXXDefaultArgExprBits.Loc = ReadSourceLocation(); } void ASTStmtReader::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { VisitExpr(E); E->Field = ReadDeclAs(); + E->UsedContext = ReadDeclAs(); E->CXXDefaultInitExprBits.Loc = ReadSourceLocation(); } @@ -2651,6 +2662,10 @@ S = new (Context) VAArgExpr(Empty); break; + case EXPR_SOURCE_LOC: + S = new (Context) SourceLocExpr(Empty); + break; + case EXPR_ADDR_LABEL: S = new (Context) AddrLabelExpr(Empty); break; Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -909,6 +909,15 @@ Code = serialization::EXPR_VA_ARG; } +void ASTStmtWriter::VisitSourceLocExpr(SourceLocExpr *E) { + VisitExpr(E); + Record.AddDeclRef(cast_or_null(E->getParentContext())); + Record.AddSourceLocation(E->getBeginLoc()); + Record.AddSourceLocation(E->getEndLoc()); + Record.push_back(E->getIdentKind()); + Code = serialization::EXPR_SOURCE_LOC; +} + void ASTStmtWriter::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getAmpAmpLoc()); @@ -1468,6 +1477,7 @@ void ASTStmtWriter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { VisitExpr(E); Record.AddDeclRef(E->getParam()); + Record.AddDeclRef(cast_or_null(E->getUsedContext())); Record.AddSourceLocation(E->getUsedLocation()); Code = serialization::EXPR_CXX_DEFAULT_ARG; } @@ -1475,6 +1485,7 @@ void ASTStmtWriter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { VisitExpr(E); Record.AddDeclRef(E->getField()); + Record.AddDeclRef(cast_or_null(E->getUsedContext())); Record.AddSourceLocation(E->getExprLoc()); Code = serialization::EXPR_CXX_DEFAULT_INIT; } Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1341,6 +1341,7 @@ case Stmt::NoInitExprClass: case Stmt::SizeOfPackExprClass: case Stmt::StringLiteralClass: + case Stmt::SourceLocExprClass: case Stmt::ObjCStringLiteralClass: case Stmt::CXXPseudoDestructorExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: Index: test/CodeGenCXX/builtin-source-location.cpp =================================================================== --- test/CodeGenCXX/builtin-source-location.cpp +++ test/CodeGenCXX/builtin-source-location.cpp @@ -0,0 +1,268 @@ +// RUN: %clang_cc1 -std=c++2a -fblocks %s -triple %itanium_abi_triple -emit-llvm -o %t.ll + +#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; + +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 { + source_location __loc; + __loc.set(__line, __col, __file, __func); + 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; + } + 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; } +}; + +using SL = 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]] +#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: @[[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: define internal void @__cxx_global_var_init() +// CHECK-GLOBAL-TWO-NOT: ret +// CHECK-GLOBAL-TWO: call void @_ZN15source_location11bad_currentEjjPKcS1_(%struct.source_location* sret @runtime_init_global, +// CHECK-GLOBAL-TWO-SAME: i32 1100, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], +#line 1100 "test_runtime_init.cpp" +SL runtime_init_global = SL::bad_current(); + +#line 2000 "test_function.cpp" +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: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %local, +// CHECK-LOCAL-ONE-SAME: i32 2100, i32 {{[0-9]+}}, +// CHECK-LOCAL-ONE-SAME: {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], +#line 2100 "test_current.cpp" + SL local = SL::current(); +} + +#line 3000 "TestInitClass.cpp" +struct TestInit { + SL info = SL::current(); + SL arg_info; + +#line 3100 "TestInitCtor.cpp" + TestInit(SL arg_info = SL::current()) : arg_info(arg_info) {} +}; + +// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-CTOR-GLOBAL +// +// 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: 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 %[[TMP_ONE:[^,]*]], +// CHECK-CTOR-GLOBAL-SAME: i32 3400, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], +// CHECK-CTOR-GLOBAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* @GlobalInitVal, %struct.source_location* {{[^%]*}}%[[TMP_ONE]]) +#line 3400 "GlobalInitVal.cpp" +TestInit GlobalInitVal; + +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: define void @test_init_function() +// CHECK-CTOR-LOCAL-NOT: ret +// +// CHECK-CTOR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP:[^,]*]], +// CHECK-CTOR-LOCAL-SAME: i32 3500, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]], +// CHECK-CTOR-LOCAL-NEXT: call void @_ZN8TestInitC1E15source_location(%struct.TestInit* %init_local, %struct.source_location* {{[^%]*}}%[[TMP]]) +#line 3500 "LocalInitVal.cpp" + TestInit init_local; + sink(init_local); +} + +#line 4000 "ConstexprClass.cpp" +struct TestInitConstexpr { + SL info = SL::current(); + SL arg_info; +#line 4200 "ConstexprCtor.cpp" + constexpr TestInitConstexpr(SL arg_info = SL::current()) : arg_info(arg_info) {} +}; + +// 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: @[[FILE_ARG:.*]] = {{.*}}c"ConstexprGlobal.cpp\00" +// CHECK-CONSTEXPR-T2-DAG: @[[EMPTY:.*]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +// +// 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]] +#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: @[[FILE:.*]] = {{.*}}c"ConstexprLocal.cpp\00" +// +// CHECK-CONSTEXPR-LOCAL: define void @test_init_function_constexpr() +// CHECK-CONSTEXPR-LOCAL: call void @_ZN15source_location7currentEjjPKcS1_(%struct.source_location* sret %[[TMP:[^,]*]], +// CHECK-CONSTEXPR-LOCAL-SAME: i32 4600, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] +// CHECK-CONSTEXPR-LOCAL: call void @_ZN17TestInitConstexprC1E15source_location(%struct.TestInitConstexpr* %local_val, {{.*}}%[[TMP]]) +#line 4600 "ConstexprLocal.cpp" + TestInitConstexpr local_val; +} + +#line 5000 "TestInitAgg.cpp" +struct TestInitAgg { +#line 5100 "i1.cpp" + SL i1; +#line 5200 "i2.cpp" + SL i2 = SL::current(); +#line 5300 "TestInitAggEnd.cpp" +}; + +// 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: @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]] +#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 %[[I2]], +// CHECK-AGG-BRACE-SAME: i32 5700, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] +#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 %[[I2]], +// CHECK-AGG-EQUAL-SAME: i32 5900, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] +#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 %[[I1]], +// CHECK-AGG-LIST-SAME: i32 6100, i32 {{[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 %[[I2]], +// CHECK-AGG-LIST-SAME: i32 6200, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE_DEFAULT]], {{[^@]*}}@[[FUNC]] +#line 6000 "InitListStart.cpp" + TestInitAgg local_list_init = + { +#line 6100 "ListElem.cpp" + {SL::current()} +#line 6200 "InitListEnd.cpp" + }; +} + +#line 7000 "TestTemplate.cpp" +template +struct TestTemplate { + Tp info = Tp::current(); + Tp arg_info; +#line 7100 "TestTemplateCtor.cpp" + constexpr TestTemplate(Tp arg_info = Tp::current()) : arg_info(arg_info) {} +}; + +#line 7200 "test_template.cpp" +template +void test_template() { + +// RUN: FileCheck --input-file %t.ll %s --check-prefix=CHECK-TEMPL -DINT_ID=0 +// 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 %[[TMP:[^,]*]], +// CHECK-TEMPL-SAME: i32 7300, i32 {{[0-9]+}}, {{[^@]*}}@[[FILE]], {{[^@]*}}@[[FUNC]] +#line 7300 "local_templ.cpp" + TestTemplate local_templ; +} +#line 7400 "EndTestTemplate.cpp" +template void test_template(); +template void test_template(); Index: test/CodeGenCXX/builtin_FUNCTION.cpp =================================================================== --- test/CodeGenCXX/builtin_FUNCTION.cpp +++ test/CodeGenCXX/builtin_FUNCTION.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -std=c++2a -fblocks %s -triple %itanium_abi_triple -emit-llvm -o %t.ll +// RUN: FileCheck --input-file %t.ll %s + +namespace test_func { + +constexpr const char *test_default_arg(const char *f = __builtin_FUNCTION()) { + return f; +} +// CHECK: @[[EMPTY_STR:.+]] = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 + +// CHECK: @_ZN9test_func6globalE = global i8* getelementptr inbounds ([1 x i8], [1 x i8]* @[[EMPTY_STR]], i32 0, i32 0), align 8 +const char *global = test_default_arg(); + +// CHECK: @_ZN9test_func10global_twoE = global i8* getelementptr inbounds ([1 x i8], [1 x i8]* @[[EMPTY_STR]], i32 0, i32 0), align 8 +const char *global_two = __builtin_FUNCTION(); + +const char * const global_three = test_default_arg(); + +// CHECK: @[[STR_ONE:.+]] = private unnamed_addr constant [14 x i8] c"test_func_one\00", align 1 +// CHECK: @[[STR_TWO:.+]] = private unnamed_addr constant [14 x i8] c"test_func_two\00", align 1 +// CHECK: @[[STR_THREE:.+]] = private unnamed_addr constant [20 x i8] c"do_default_arg_test\00", align 1 + +// CHECK: define i8* @_ZN9test_func13test_func_oneEv() +// CHECK: ret i8* getelementptr inbounds ([14 x i8], [14 x i8]* @[[STR_ONE]], i32 0, i32 0) +const char *test_func_one() { + return __builtin_FUNCTION(); +} + +// CHECK: define i8* @_ZN9test_func13test_func_twoEv() +// CHECK: ret i8* getelementptr inbounds ([14 x i8], [14 x i8]* @[[STR_TWO]], i32 0, i32 0) +const char *test_func_two() { + return __builtin_FUNCTION(); +} + +// CHECK: define void @_ZN9test_func19do_default_arg_testEv() +// CHECK: %call = call i8* @_ZN9test_func16test_default_argEPKc(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @[[STR_THREE]], i32 0, i32 0)) +void do_default_arg_test() { + test_default_arg(); +} + +} // namespace test_func Index: test/CodeGenCXX/builtin_LINE.cpp =================================================================== --- test/CodeGenCXX/builtin_LINE.cpp +++ test/CodeGenCXX/builtin_LINE.cpp @@ -0,0 +1,95 @@ +// RUN: %clang_cc1 -std=c++1z -fblocks %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s + +extern "C" int sink; +extern "C" const volatile void* volatile ptr_sink = nullptr; + +struct Tag1 {}; +struct Tag2 {}; +struct Tag3 {}; +struct Tag4 {}; + +constexpr int get_line_constexpr(int l = __builtin_LINE()) { + return l; +} + +int get_line_nonconstexpr(int l = __builtin_LINE()) { + return l; +} + + +int get_line(int l = __builtin_LINE()) { + return l; +} + +int get_line2(int l = get_line()) { return l; } + + +// CHECK: @global_one = global i32 [[@LINE+1]], align 4 +int global_one = __builtin_LINE(); +// CHECK-NEXT: @global_two = global i32 [[@LINE+1]], align 4 +int global_two = get_line_constexpr(); +// CHECK: @_ZL12global_three = internal constant i32 [[@LINE+1]], align 4 +const int global_three(get_line_constexpr()); + +// CHECK-LABEL: define internal void @__cxx_global_var_init +// CHECK: %call = call i32 @_Z21get_line_nonconstexpri(i32 [[@LINE+2]]) +// CHECK-NEXT: store i32 %call, i32* @global_four, align 4 +int global_four = get_line_nonconstexpr(); + +struct InClassInit { + int Init = __builtin_LINE(); + int Init2 = get_line2(); + InClassInit(); + constexpr InClassInit(Tag1, int l = __builtin_LINE()) : Init(l), Init2(l) {} + constexpr InClassInit(Tag2) : Init(__builtin_LINE()), Init2(__builtin_LINE()) {} + InClassInit(Tag3, int l = __builtin_LINE()); + InClassInit(Tag4, int l = get_line2()); + + static void test_class(); +}; +// CHECK-LABEL: define void @_ZN11InClassInit10test_classEv() +void InClassInit::test_class() { + // CHECK: call void @_ZN11InClassInitC1Ev(%struct.InClassInit* %test_one) + InClassInit test_one; + // CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag1i(%struct.InClassInit* %test_two, i32 [[@LINE+1]]) + InClassInit test_two{Tag1{}}; + // CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag2(%struct.InClassInit* %test_three) + InClassInit test_three{Tag2{}}; + // CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag3i(%struct.InClassInit* %test_four, i32 [[@LINE+1]]) + InClassInit test_four(Tag3{}); + // CHECK-NEXT: %[[CALL:.+]] = call i32 @_Z8get_linei(i32 [[@LINE+3]]) + // CHECK-NEXT: %[[CALL2:.+]] = call i32 @_Z9get_line2i(i32 %[[CALL]]) + // CHECK-NEXT: call void @_ZN11InClassInitC1E4Tag4i(%struct.InClassInit* %test_five, i32 %[[CALL2]]) + InClassInit test_five(Tag4{}); + +} +// CHECK-LABEL: define void @_ZN11InClassInitC2Ev +// CHECK: store i32 [[@LINE+4]], i32* %Init, align 4 +// CHECK: %call = call i32 @_Z8get_linei(i32 [[@LINE+3]]) +// CHECK-NEXT: %call2 = call i32 @_Z9get_line2i(i32 %call) +// CHECK-NEXT: store i32 %call2, i32* %Init2, align 4 +InClassInit::InClassInit() = default; + +InClassInit::InClassInit(Tag3, int l) : Init(l) {} + +// CHECK-LABEL: define void @_ZN11InClassInitC2E4Tag4i(%struct.InClassInit* %this, i32 %arg) +// CHECK: %[[TEMP:.+]] = load i32, i32* %arg.addr, align 4 +// CHECK-NEXT: store i32 %[[TEMP]], i32* %Init, align 4 +// CHECK: %[[CALL:.+]] = call i32 @_Z8get_linei(i32 [[@LINE+3]]) +// CHECK-NEXT: %[[CALL2:.+]] = call i32 @_Z9get_line2i(i32 %[[CALL]]) +// CHECK-NEXT: store i32 %[[CALL2]], i32* %Init2, align 4 +InClassInit::InClassInit(Tag4, int arg) : Init(arg) {} + +// CHECK-LABEL: define void @_Z13get_line_testv() +void get_line_test() { + // CHECK: %[[CALL:.+]] = call i32 @_Z8get_linei(i32 [[@LINE+2]]) + // CHECK-NEXT: store i32 %[[CALL]], i32* @sink, align 4 + sink = get_line(); + // CHECK-NEXT: store i32 [[@LINE+1]], i32* @sink, align 4 + sink = __builtin_LINE(); + ptr_sink = &global_three; +} + +void foo() { + const int N[] = {__builtin_LINE(), get_line_constexpr()}; +} Index: test/CodeGenCXX/debug-info-line.cpp =================================================================== --- test/CodeGenCXX/debug-info-line.cpp +++ test/CodeGenCXX/debug-info-line.cpp @@ -293,6 +293,13 @@ f24_a(); } +// CHECK-LABEL: define +void f25_a(int x = __builtin_LINE()) {} +void f25() { + // CHECK: call void @_Z5f25_ai(i32 2700) +#line 2700 + f25_a(); +} // CHECK: [[DBG_F1]] = !DILocation(line: 100, // CHECK: [[DBG_FOO_VALUE]] = !DILocation(line: 200, // CHECK: [[DBG_FOO_REF]] = !DILocation(line: 202, Index: test/Parser/builtin_source_location.c =================================================================== --- test/Parser/builtin_source_location.c +++ test/Parser/builtin_source_location.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int main() { + int line = __builtin_LINE(); + __builtin_LINE(42); // expected-error {{expected ')'}} + __builtin_LINE(double); // expected-error {{expected ')'}} + + int column = __builtin_COLUMN(); + __builtin_COLUMN(42); // expected-error {{expected ')'}} + __builtin_COLUMN(double); // expected-error {{expected ')'}} + + const char *func = __builtin_FUNCTION(); + __builtin_FUNCTION(42); // expected-error {{expected ')'}} + __builtin_FUNCTION(double); // expected-error {{expected ')'}} + + const char *file = __builtin_FILE(); + __builtin_FILE(42); // expected-error {{expected ')'}} + __builtin_FILE(double); // expected-error {{expected ')'}} +} Index: test/Sema/source_location.c =================================================================== --- test/Sema/source_location.c +++ test/Sema/source_location.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -std=c90 -fconst-strings -DCONST_STRINGS -verify %s +// RUN: %clang_cc1 -std=c90 -verify %s + +// expected-no-diagnostics + +#define IsEqual(L, R) (__builtin_strcmp(L, R) == 0) + +const char *const FILE = __builtin_FILE(); +const char *const FUNC = __builtin_FUNCTION(); +const unsigned LINE = __builtin_LINE(); +const unsigned COL = __builtin_COLUMN(); + +#ifndef CONST_STRINGS +char *const NCFILE = __builtin_FILE(); +char *const NCFUNC = __builtin_FUNCTION(); +#endif + +#ifdef CONST_STRINGS +_Static_assert(IsEqual(__builtin_FILE(), __FILE__), ""); +_Static_assert(__builtin_LINE() == __LINE__, ""); +_Static_assert(IsEqual("", __builtin_FUNCTION()), ""); + +#line 42 "my_file.c" +_Static_assert(__builtin_LINE() == 42, ""); +_Static_assert(IsEqual(__builtin_FILE(), "my_file.c"), ""); + +_Static_assert(__builtin_COLUMN() == __builtin_strlen("_Static_assert(_"), ""); + +void foo() { + _Static_assert(IsEqual(__builtin_FUNCTION(), "foo"), ""); +} +#endif // CONST_STRINGS Index: test/SemaCXX/Inputs/source-location-file.h =================================================================== --- test/SemaCXX/Inputs/source-location-file.h +++ test/SemaCXX/Inputs/source-location-file.h @@ -0,0 +1,44 @@ + +// NOTE: source_location.cpp must include this file after defining +// std::source_location. +namespace source_location_file { + +constexpr const char *FILE = __FILE__; + +constexpr SL global_info = SL::current(); + +constexpr SL test_function(SL v = SL::current()) { + return v; +} + +constexpr SL test_function_indirect() { + return test_function(); +} + +template +constexpr U test_function_template(T, U u = U::current()) { + return u; +} + +template +constexpr U test_function_template_indirect(T t) { + return test_function_template(t); +} + +struct TestClass { + SL info = SL::current(); + SL ctor_info; + TestClass() = default; + constexpr TestClass(int, SL cinfo = SL::current()) : ctor_info(cinfo) {} + template + constexpr TestClass(int, T, U u = U::current()) : ctor_info(u) {} +}; + +template +struct AggrClass { + int x; + T info; + T init_info = T::current(); +}; + +} // namespace source_location_file Index: test/SemaCXX/source_location.cpp =================================================================== --- test/SemaCXX/source_location.cpp +++ test/SemaCXX/source_location.cpp @@ -0,0 +1,590 @@ +// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify %s +// expected-no-diagnostics + +#define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42) +#define CURRENT_FROM_MACRO() SL::current() +#define FORWARD(...) __VA_ARGS__ + +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; +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 { + source_location __loc; + __loc.__m_line = __line; + __loc.__m_col = __col; + __loc.__m_file = __file; + __loc.__m_func = __func; + 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; } +}; +} // namespace experimental +} // namespace std + +using SL = std::experimental::source_location; + +#include "Inputs/source-location-file.h" +namespace SLF = source_location_file; + +constexpr bool is_equal(const char *LHS, const char *RHS) { + while (*LHS != 0 && *RHS != 0) { + if (*LHS != *RHS) + return false; + ++LHS; + ++RHS; + } + return *LHS == 0 && *RHS == 0; +} + +template +constexpr T identity(T t) { + return t; +} + +template +struct Pair { + T first; + U second; +}; + +template +constexpr bool is_same = false; +template +constexpr bool is_same = true; + +// test types +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())); + +//===----------------------------------------------------------------------===// +// __builtin_LINE() +//===----------------------------------------------------------------------===// + +namespace test_line { +static_assert(SL::current().line() == __LINE__); +static_assert(SL::current().line() == CURRENT_FROM_MACRO().line()); + +static constexpr SL GlobalS = SL::current(); + +static_assert(GlobalS.line() == __LINE__ - 2); + +// clang-format off +constexpr bool test_line_fn() { + constexpr SL S = SL::current(); + static_assert(S.line() == (__LINE__ - 1), ""); + // The start of the call expression to `current()` begins at the token `SL` + constexpr int ExpectLine = __LINE__ + 3; + constexpr SL S2 + = + SL // Call expression starts here + :: + current + ( + + ) + ; + static_assert(S2.line() == ExpectLine, ""); + + static_assert( + FORWARD( + __builtin_LINE + ( + ) + ) + == __LINE__ - 1, ""); + static_assert(\ +\ + __builtin_LINE()\ +\ + == __LINE__ - 2, ""); + static_assert(\ + _\ +_builtin_LINE() + == __LINE__ - 2, ""); + + return true; +} +// clang-format on +static_assert(test_line_fn()); + +static_assert(__builtin_LINE() == __LINE__, ""); + +constexpr int baz() { return 101; } + +constexpr int test_line_fn_simple(int z = baz(), int x = __builtin_LINE()) { + return x; +} +void bar() { + static_assert(test_line_fn_simple() == __LINE__, ""); + static_assert(test_line_fn_simple() == __LINE__, ""); +} + +struct CallExpr { + constexpr int operator()(int x = __builtin_LINE()) const { return x; } +}; +constexpr CallExpr get_call() { return CallExpr{}; } +static_assert(get_call()() == __LINE__, ""); + +template +constexpr bool test_line_fn_template(T Expect, int L = __builtin_LINE()) { + return Expect == L; +} +static_assert(test_line_fn_template(__LINE__)); + +struct InMemInit { + constexpr bool check(int expect) const { + return info.line() == expect; + } + SL info = SL::current(); + InMemInit() = default; + constexpr InMemInit(int) {} +}; +static_assert(InMemInit{}.check(__LINE__ - 3), ""); +static_assert(InMemInit{42}.check(__LINE__ - 3), ""); + +template +struct InMemInitTemplate { + constexpr bool check(int expect) const { + return info.line() == expect; + } + U info = U::current(); + InMemInitTemplate() = default; + constexpr InMemInitTemplate(T) {} + constexpr InMemInitTemplate(T, T) : info(U::current()) {} + template constexpr InMemInitTemplate(T, T, T, V info = U::current()) + : info(info) {} +}; +void test_mem_init_template() { + constexpr int line_offset = 8; + static_assert(InMemInitTemplate{}.check(__LINE__ - line_offset), ""); + static_assert(InMemInitTemplate{42}.check(__LINE__ - line_offset), ""); + static_assert(InMemInitTemplate{42, 42}.check(__LINE__ - line_offset), ""); + static_assert(InMemInitTemplate{42, 42, 42}.check(__LINE__), ""); +} + +struct AggInit { + int x; + int y = __builtin_LINE(); + constexpr bool check(int expect) const { + return y == expect; + } +}; +constexpr AggInit AI{42}; +static_assert(AI.check(__LINE__ - 1), ""); + +template +struct AggInitTemplate { + constexpr bool check(int expect) const { + return expect == info.line(); + } + T x; + U info = U::current(); +}; + +template +constexpr U test_fn_template(T, U u = U::current()) { + return u; +} +void fn_template_tests() { + static_assert(test_fn_template(42).line() == __LINE__, ""); +} + +struct TestMethodTemplate { + template + constexpr U get(T, U u = U::current(), U2 u2 = identity(U2::current())) const { + assert(u.line() == u2.line()); + return u; + } +}; +void method_template_tests() { + static_assert(TestMethodTemplate{}.get(42).line() == __LINE__, ""); +} + +struct InStaticInit { + static constexpr int LINE = __LINE__; + static constexpr const int x1 = __builtin_LINE(); + static constexpr const int x2 = identity(__builtin_LINE()); + static const int x3; + const int x4 = __builtin_LINE(); + int x5 = __builtin_LINE(); +}; +const int InStaticInit::x3 = __builtin_LINE(); +static_assert(InStaticInit::x1 == InStaticInit::LINE + 1, ""); +static_assert(InStaticInit::x2 == InStaticInit::LINE + 2, ""); + +template +constexpr void check_fn_template_param(T) { + constexpr int RealExpect = Expect == -1 ? __LINE__ - 2 : Expect; + static_assert(N == RealExpect); +} +template void check_fn_template_param(int); +template void check_fn_template_param(long); + +#line 100 +struct AggBase { +#line 200 + int x = __builtin_LINE(); + int y = __builtin_LINE(); + int z = __builtin_LINE(); +}; +#line 300 +struct AggDer : AggBase { +}; +#line 400 +static_assert(AggDer{}.x == 400, ""); + +struct ClassBase { +#line 400 + int x = __builtin_LINE(); + int y = 0; + int z = 0; +#line 500 + ClassBase() = default; + constexpr ClassBase(int yy, int zz = __builtin_LINE()) + : y(yy), z(zz) {} +}; +struct ClassDer : ClassBase { +#line 600 + ClassDer() = default; + constexpr ClassDer(int yy) : ClassBase(yy) {} + constexpr ClassDer(int yy, int zz) : ClassBase(yy, zz) {} +}; +#line 700 +static_assert(ClassDer{}.x == 500, ""); +static_assert(ClassDer{42}.x == 501, ""); +static_assert(ClassDer{42}.z == 601, ""); +static_assert(ClassDer{42, 42}.x == 501, ""); + +struct ClassAggDer : AggBase { +#line 800 + ClassAggDer() = default; + constexpr ClassAggDer(int, int x = __builtin_LINE()) : AggBase{x} {} +}; +static_assert(ClassAggDer{}.x == 100, ""); + +} // namespace test_line + +//===----------------------------------------------------------------------===// +// __builtin_FILE() +//===----------------------------------------------------------------------===// + +namespace test_file { +constexpr const char *test_file_simple(const char *__f = __builtin_FILE()) { + return __f; +} +void test_function() { +#line 900 + static_assert(is_equal(test_file_simple(), __FILE__)); + static_assert(is_equal(SLF::test_function().file(), __FILE__), ""); + static_assert(is_equal(SLF::test_function_template(42).file(), __FILE__), ""); + + static_assert(is_equal(SLF::test_function_indirect().file(), SLF::global_info.file()), ""); + static_assert(is_equal(SLF::test_function_template_indirect(42).file(), SLF::global_info.file()), ""); + + static_assert(test_file_simple() != nullptr); + static_assert(!is_equal(test_file_simple(), "source_location.cpp")); +} + +void test_class() { +#line 315 + using SLF::TestClass; + constexpr TestClass Default; + constexpr TestClass InParam{42}; + constexpr TestClass Template{42, 42}; + constexpr auto *F = Default.info.file(); + constexpr auto Char = F[0]; + static_assert(is_equal(Default.info.file(), SLF::FILE), ""); + static_assert(is_equal(InParam.info.file(), SLF::FILE), ""); + static_assert(is_equal(InParam.ctor_info.file(), __FILE__), ""); +} + +void test_aggr_class() { + using Agg = SLF::AggrClass<>; + constexpr Agg Default{}; + constexpr Agg InitOne{42}; + static_assert(is_equal(Default.init_info.file(), __FILE__), ""); + static_assert(is_equal(InitOne.init_info.file(), __FILE__), ""); +} + +} // namespace test_file + +//===----------------------------------------------------------------------===// +// __builtin_FUNCTION() +//===----------------------------------------------------------------------===// + +namespace test_func { + +constexpr const char *test_func_simple(const char *__f = __builtin_FUNCTION()) { + return __f; +} +constexpr const char *get_function() { + return __func__; +} +constexpr bool test_function() { + return is_equal(__func__, test_func_simple()) && + !is_equal(get_function(), test_func_simple()); +} +static_assert(test_function()); + +template +constexpr Pair test_func_template(T, U u = U::current()) { + static_assert(is_equal(__func__, U::current().function())); + return {u, U::current()}; +} +template +void func_template_tests() { + constexpr auto P = test_func_template(42); + //static_assert(is_equal(P.first.function(), __func__), ""); + //static_assert(!is_equal(P.second.function(), __func__), ""); +} +template void func_template_tests(); + +template +struct TestCtor { + T info = T::current(); + T ctor_info; + TestCtor() = default; + template + constexpr TestCtor(int, U u = U::current()) : ctor_info(u) {} +}; +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__)); +} + +constexpr SL global_sl = SL::current(); +static_assert(is_equal(global_sl.function(), "")); + +} // namespace test_func + +//===----------------------------------------------------------------------===// +// __builtin_COLUMN() +//===----------------------------------------------------------------------===// + +namespace test_column { + +// clang-format off +constexpr bool test_column_fn() { + constexpr SL S = SL::current(); + static_assert(S.line() == (__LINE__ - 1), ""); + constexpr int Indent = 4; + { + // The start of the call expression to `current()` begins at the token `SL` + constexpr int ExpectCol = Indent + 3; + constexpr SL S2 + = + SL // Call expression starts here + :: + current + ( + + ) + ; + static_assert(S2.column() == ExpectCol, ""); + } + { + constexpr int ExpectCol = 2; + constexpr int C = + __builtin_COLUMN // Expect call expression to start here + (); + static_assert(C == ExpectCol); + } + return true; +} +#line 420 +static_assert(test_column_fn()); + +// Test that the column matches the start of the call expression 'SL::current()' +static_assert(SL::current().column() == __builtin_strlen("static_assert(S")); +struct TestClass { + int x = __builtin_COLUMN(); + TestClass() = default; /* indented to 3 spaces for testing */ + constexpr TestClass(int, int o = __builtin_COLUMN()) : x(o) {} +}; +struct TestAggClass { + int x = __builtin_COLUMN(); +}; +constexpr bool test_class() { + + auto check = [](int V, const char* S, int indent = 4) { + assert(V == (__builtin_strlen(S) + indent)); + }; + { + TestClass t{}; + check(t.x, " T", 0); // Start of default constructor decl. + } + { + TestClass t1 + {42}; + check(t1.x, "TestClass t"); // Start of variable being constructed. + } + { + TestAggClass t { }; + check(t.x, "TestAggClass t { }"); + } + { + TestAggClass t = { }; + check(t.x, "TestAggClass t = { }"); + } + return true; +} +static_assert(test_class()); +// clang-format on +} // namespace test_column + +// Test [reflection.src_loc.creation]p2 +// > The value should be affected by #line (C++14 16.4) in the same manner as +// > for __LINE__ and __FILE__. +namespace test_pragma_line { +constexpr int StartLine = 42; +#line 42 +static_assert(__builtin_LINE() == StartLine); +static_assert(__builtin_LINE() == StartLine + 1); +static_assert(SL::current().line() == StartLine + 2); +#line 44 "test_file.c" +static_assert(is_equal("test_file.c", __FILE__)); +static_assert(is_equal("test_file.c", __builtin_FILE())); +static_assert(is_equal("test_file.c", SL::current().file())); +static_assert(is_equal("test_file.c", SLF::test_function().file())); +static_assert(is_equal(SLF::FILE, SLF::test_function_indirect().file())); +} // end namespace test_pragma_line + +namespace test_out_of_line_init { +#line 4000 "test_out_of_line_init.cpp" +constexpr unsigned get_line(unsigned n = __builtin_LINE()) { return n; } +constexpr const char *get_file(const char *f = __builtin_FILE()) { return f; } +constexpr const char *get_func(const char *f = __builtin_FUNCTION()) { return f; } +#line 4100 "A.cpp" +struct A { + int n = __builtin_LINE(); + int n2 = get_line(); + const char *f = __builtin_FILE(); + const char *f2 = get_file(); + const char *func = __builtin_FUNCTION(); + const char *func2 = get_func(); + SL info = SL::current(); +}; +#line 4200 "B.cpp" +struct B { + A a = {}; +}; +#line 4300 "test_passed.cpp" +constexpr B b = {}; +static_assert(b.a.n == 4300, ""); +static_assert(b.a.n2 == 4300, ""); +static_assert(b.a.info.line() == 4300, ""); +static_assert(is_equal(b.a.f, "test_passed.cpp")); +static_assert(is_equal(b.a.f2, "test_passed.cpp")); +static_assert(is_equal(b.a.info.file(), "test_passed.cpp")); +static_assert(is_equal(b.a.func, "")); +static_assert(is_equal(b.a.func2, "")); +static_assert(is_equal(b.a.info.function(), "")); + +constexpr bool test_in_func() { +#line 4400 "test_func_passed.cpp" + constexpr B b = {}; + static_assert(b.a.n == 4400, ""); + static_assert(b.a.n2 == 4400, ""); + static_assert(b.a.info.line() == 4400, ""); + static_assert(is_equal(b.a.f, "test_func_passed.cpp")); + static_assert(is_equal(b.a.f2, "test_func_passed.cpp")); + 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")); + return true; +} +static_assert(test_in_func()); + +} // end namespace test_out_of_line_init + +namespace test_global_scope { +#line 5000 "test_global_scope.cpp" +constexpr unsigned get_line(unsigned n = __builtin_LINE()) { return n; } +constexpr const char *get_file(const char *f = __builtin_FILE()) { return f; } +constexpr const char *get_func(const char *f = __builtin_FUNCTION()) { return f; } +#line 5100 +struct InInit { + unsigned l = get_line(); + const char *f = get_file(); + const char *func = get_func(); + +#line 5200 "in_init.cpp" + constexpr InInit() {} +}; +#line 5300 +constexpr InInit II; + +static_assert(II.l == 5200, ""); +static_assert(is_equal(II.f, "in_init.cpp")); +static_assert(is_equal(II.func, "InInit")); + +#line 5400 +struct AggInit { + unsigned l = get_line(); + const char *f = get_file(); + const char *func = get_func(); +}; +#line 5500 "brace_init.cpp" +constexpr AggInit AI = {}; +static_assert(AI.l == 5500); +static_assert(is_equal(AI.f, "brace_init.cpp")); +static_assert(is_equal(AI.func, "")); + +} // namespace test_global_scope + +namespace TestFuncInInit { +#line 6000 "InitClass.cpp" +struct Init { + SL info; +#line 6100 "InitCtor.cpp" + constexpr Init(SL info = SL::current()) : info(info) {} +}; +#line 6200 "InitGlobal.cpp" +constexpr Init I; +static_assert(I.info.line() == 6200); +static_assert(is_equal(I.info.file(), "InitGlobal.cpp")); + +} // namespace TestFuncInInit + +namespace TestConstexprContext { +#line 7000 "TestConstexprContext.cpp" + constexpr const char* foo() { return __builtin_FILE(); } +#line 7100 "Bar.cpp" + constexpr const char* bar(const char* x = foo()) { return x; } + constexpr bool test() { + static_assert(is_equal(bar(), "TestConstexprContext.cpp")); + return true; + } + static_assert(test()); +} Index: tools/libclang/CXCursor.cpp =================================================================== --- tools/libclang/CXCursor.cpp +++ tools/libclang/CXCursor.cpp @@ -282,6 +282,7 @@ case Stmt::ParenListExprClass: case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: + case Stmt::SourceLocExprClass: case Stmt::ConvertVectorExprClass: case Stmt::VAArgExprClass: case Stmt::ObjCArrayLiteralClass: