Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -2144,6 +2144,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 appears 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/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -3833,6 +3833,85 @@ } }; +/// SourceLocExpr - Represents a function call to one of +/// __builtin_LINE(), __builtin_COLUMN(), __builtin_FUNCTION(), or +/// __builtin_FILE() +class SourceLocExpr final : public Expr { +public: + enum IdentType { Function, File, Line, Column }; + +private: + SourceLocation BuiltinLoc, RParenLoc; + DeclContext *ParentContext; + +public: + static QualType BuildSourceLocExprType(const ASTContext &Ctx, IdentType Type, + bool MakeArray = false, + int ArraySize = -1); + SourceLocExpr(IdentType Type, SourceLocation BLoc, SourceLocation RParenLoc, + QualType Ty, DeclContext *Context); + + /// \brief Build an empty call expression. + explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {} + + /// \brief Return a string representing the name of the specific builtin + /// function. + StringRef getBuiltinStr() const LLVM_READONLY; + + IdentType getIdentType() const LLVM_READONLY { + return static_cast(SourceLocExprBits.Type); + } + + bool isLineOrColumn() const LLVM_READONLY { + return getIdentType() == Line || getIdentType() == Column; + } + + llvm::APInt getIntValue(const ASTContext &Ctx, SourceLocation Loc) const; + llvm::APInt getIntValue(const ASTContext &Ctx) const { + return getIntValue(Ctx, BuiltinLoc); + } + + StringLiteral *getStringValue(const ASTContext &Ctx, SourceLocation Loc, + const DeclContext *UsedContext) const; + StringLiteral *getStringValue(const ASTContext &Ctx) const { + return getStringValue(Ctx, BuiltinLoc, ParentContext); + } + Expr *getValue(const ASTContext &Ctx, SourceLocation Loc, + const DeclContext *UsedContext) const; + Expr *getValue(const ASTContext &Ctx) const { + return getValue(Ctx, BuiltinLoc, ParentContext); + } + + /// \brief 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 LLVM_READONLY { return BuiltinLoc; } + SourceLocation getLocStart() const LLVM_READONLY { return BuiltinLoc; } + SourceLocation getLocEnd() const LLVM_READONLY { 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; + + void setIdentType(IdentType T) { SourceLocExprBits.Type = T; } + void setParentContext(DeclContext *DC) { ParentContext = DC; } + void setLocStart(SourceLocation L) { BuiltinLoc = L; } + void setLocEnd(SourceLocation L) { RParenLoc = L; } +}; + /// @brief 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 @@ -1043,18 +1043,22 @@ /// \brief The parameter whose default is being used. ParmVarDecl *Param; + /// \brief The context where the default argument expression was used. + DeclContext *UsedContext; + /// \brief The location where the default argument expression was used. SourceLocation Loc; - CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *param) + CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *param, + DeclContext *UsedContext) : Expr(SC, param->hasUnparsedDefaultArg() - ? param->getType().getNonReferenceType() - : param->getDefaultArg()->getType(), + ? param->getType().getNonReferenceType() + : param->getDefaultArg()->getType(), param->getDefaultArg()->getValueKind(), param->getDefaultArg()->getObjectKind(), false, false, false, false), - Param(param), Loc(Loc) {} + Param(param), UsedContext(UsedContext), Loc(Loc) {} public: friend class ASTStmtReader; @@ -1065,8 +1069,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. @@ -1081,6 +1087,8 @@ return getParam()->getDefaultArg(); } + const DeclContext *getUsedContext() const { return UsedContext; } + DeclContext *getUsedContext() { return UsedContext; } /// \brief Retrieve the location where this default argument was actually /// used. SourceLocation getUsedLocation() const { return Loc; } @@ -1114,11 +1122,14 @@ /// \brief The field whose default is being used. FieldDecl *Field; + /// \brief The context where the default initializer expression was used. + DeclContext *UsedContext; + /// \brief The location where the default initializer expression was used. SourceLocation Loc; CXXDefaultInitExpr(const ASTContext &C, SourceLocation Loc, FieldDecl *Field, - QualType T); + QualType T, DeclContext *UsedContext); CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {} @@ -1129,8 +1140,10 @@ /// \p Field is the non-static data member whose default initializer is used /// by this expression. static CXXDefaultInitExpr *Create(const ASTContext &C, SourceLocation Loc, - FieldDecl *Field) { - return new (C) CXXDefaultInitExpr(C, Loc, Field, Field->getType()); + FieldDecl *Field, + DeclContext *UsedContext) { + return new (C) + CXXDefaultInitExpr(C, Loc, Field, Field->getType(), UsedContext); } /// \brief Get the field whose initializer will be used. @@ -1147,6 +1160,12 @@ return Field->getInClassInitializer(); } + const DeclContext *getUsedContext() const { return UsedContext; } + DeclContext *getUsedContext() { return UsedContext; } + /// \brief Retrieve the location where this default initializer expression + /// was actually used. + SourceLocation getUsedLocation() const { return Loc; } + SourceLocation getLocStart() const LLVM_READONLY { return Loc; } SourceLocation getLocEnd() const LLVM_READONLY { return Loc; } Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2505,6 +2505,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/SourceLocExprScope.h =================================================================== --- /dev/null +++ include/clang/AST/SourceLocExprScope.h @@ -0,0 +1,83 @@ +//===--- SourceLocExprScope.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 the SourceLocExprScope utility for tracking the current +// context a source location builtin should be evaluated in. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_SOURCE_LOC_EXPR_SCOPE_H +#define LLVM_CLANG_AST_SOURCE_LOC_EXPR_SCOPE_H + +#include "clang/AST/APValue.h" +#include "clang/AST/ASTVector.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { + +template class SourceLocExprScopeBase { + bool ShouldEnable() const { + // Only update the default argument scope if we've entered a new + // evaluation context, and not when it's nested within another default + // argument. Example: + // int bar(int x = __builtin_LINE()) { return x; } + // int foo(int x = bar()) { return x; } + // static_assert(foo() == __LINE__); + return OldVal == nullptr || isa(DefaultExpr) || + !OldVal->isInSameContext(EvalContext); + } + +public: + SourceLocExprScopeBase(const Expr *DefaultExpr, SourceLocExprScopeBase **Dest, + EvalContextType *EvalContext) + : DefaultExpr(DefaultExpr), Dest(Dest), OldVal(*Dest), + EvalContext(EvalContext), Enable(false) { + if ((Enable = ShouldEnable())) + *Dest = this; + } + + ~SourceLocExprScopeBase() { + if (Enable) + *Dest = OldVal; + } + + bool isInSameContext(EvalContextType *CurEvalContext) const { + return EvalContext == CurEvalContext; + } + + SourceLocation getLoc() const { + if (const auto *DAE = dyn_cast(DefaultExpr)) + return DAE->getUsedLocation(); + return dyn_cast(DefaultExpr)->getUsedLocation(); + } + + const DeclContext *getContext() const { + if (const auto *DAE = dyn_cast(DefaultExpr)) + return DAE->getUsedContext(); + return dyn_cast(DefaultExpr)->getUsedContext(); + } + +private: + const Expr *DefaultExpr; + SourceLocExprScopeBase **Dest; + SourceLocExprScopeBase *OldVal; + EvalContextType *EvalContext; + bool Enable; +}; + +} // end namespace clang + +#endif // LLVM_CLANG_AST_SOURCE_LOC_EXPR_SCOPE_H Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -281,6 +281,16 @@ unsigned IsImplicit : 1; }; + class SourceLocExprBitfields { + friend class SourceLocExpr; + + unsigned : NumExprBits; + + /// \brief The type of source location builtin represented by the + /// SourceLocExpr. Ex. __builtin_LINE, __builtin_FUNCTION, ect. + unsigned Type : 2; + }; + union { StmtBitfields StmtBits; CompoundStmtBitfields CompoundStmtBits; @@ -298,6 +308,7 @@ InitListExprBitfields InitListExprBits; TypeTraitExprBitfields TypeTraitExprBits; CoawaitExprBitfields CoawaitBits; + SourceLocExprBitfields SourceLocExprBits; }; public: Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -91,6 +91,7 @@ def VAArgExpr : DStmt; def GenericSelectionExpr : DStmt; def PseudoObjectExpr : DStmt; +def SourceLocExpr : DStmt; // Atomic expressions def AtomicExpr : DStmt; Index: include/clang/Basic/TokenKinds.def =================================================================== --- include/clang/Basic/TokenKinds.def +++ include/clang/Basic/TokenKinds.def @@ -392,6 +392,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 @@ -4454,6 +4454,21 @@ ExprResult BuildVAArgExpr(SourceLocation BuiltinLoc, Expr *E, TypeSourceInfo *TInfo, SourceLocation RPLoc); + // __builtin_LINE(), __builtin_FUNCTION(), __builtin_FILE(), + // __builtin_COLUMN() + ExprResult ActOnSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc); + + /// \brief 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::IdentType Type, + 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 @@ -1637,6 +1637,9 @@ /// \brief A GNUNullExpr record. EXPR_GNU_NULL, + /// \brief A SourceLocExpr record + EXPR_SOURCE_LOC, + /// \brief A ShuffleVectorExpr record. EXPR_SHUFFLE_VECTOR, Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -5711,8 +5711,13 @@ if (!Param) return nullptr; - return CXXDefaultArgExpr::Create( - Importer.getToContext(), Importer.Import(E->getUsedLocation()), Param); + DeclContext *DC = Importer.ImportContext(E->getUsedContext()); + if (!DC) + return nullptr; + + return CXXDefaultArgExpr::Create(Importer.getToContext(), + Importer.Import(E->getUsedLocation()), Param, + DC); } Expr *ASTNodeImporter::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E) { @@ -6320,8 +6325,13 @@ if (!ToField && DIE->getField()) return nullptr; - return CXXDefaultInitExpr::Create( - Importer.getToContext(), Importer.Import(DIE->getLocStart()), ToField); + DeclContext *DC = Importer.ImportContext(DIE->getUsedContext()); + if (!DC) + return nullptr; + + return CXXDefaultInitExpr::Create(Importer.getToContext(), + Importer.Import(DIE->getLocStart()), + ToField, DC); } Expr *ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) { Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1831,6 +1831,7 @@ return OverOps[Opc]; } + bool BinaryOperator::isNullPointerArithmeticExtension(ASTContext &Ctx, Opcode Opc, Expr *LHS, Expr *RHS) { @@ -1863,6 +1864,110 @@ return true; } + + +SourceLocExpr::SourceLocExpr(IdentType Type, SourceLocation BLoc, + SourceLocation RParenLoc, QualType Ty, + DeclContext *ParentContext) + : Expr(SourceLocExprClass, Ty, VK_RValue, OK_Ordinary, false, false, false, + false), + BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext) { + SourceLocExprBits.Type = Type; + assert(!Ty->isDependentType() && "Type should never be dependent"); + assert(!Ty->isArrayType() && "Type should never be an array"); +} + +StringRef SourceLocExpr::getBuiltinStr() const { + switch (getIdentType()) { + case File: + return "__builtin_FILE"; + case Function: + return "__builtin_FUNCTION"; + case Line: + return "__builtin_LINE"; + case Column: + return "__builtin_COLUMN"; + } +} + +static PresumedLoc getPresumedSourceLoc(const ASTContext &Ctx, + SourceLocation Loc) { + auto &SourceMgr = Ctx.getSourceManager(); + PresumedLoc PLoc = + SourceMgr.getPresumedLoc(SourceMgr.getExpansionRange(Loc).second); + assert(PLoc.isValid()); // FIXME: Learn how to handle this. + return PLoc; +} + +llvm::APInt SourceLocExpr::getIntValue(const ASTContext &Ctx, + SourceLocation Loc) const { + auto PLoc = getPresumedSourceLoc(Ctx, Loc); + unsigned Value = [&]() { + switch (getIdentType()) { + default: + llvm_unreachable("should not be here"); + case Line: + return PLoc.getLine(); + case Column: + return PLoc.getColumn(); + } + }(); + unsigned MaxWidth = Ctx.getTargetInfo().getIntWidth(); + return llvm::APInt(MaxWidth, Value); +} + +StringLiteral * +SourceLocExpr::getStringValue(const ASTContext &Ctx, SourceLocation Loc, + const DeclContext *CurContext) const { + auto PLoc = getPresumedSourceLoc(Ctx, Loc); + auto CreateString = [&](StringRef SVal) { + QualType StrTy = BuildSourceLocExprType(Ctx, getIdentType(), + /*IsArray*/ true, SVal.size() + 1); + StringLiteral *Lit = StringLiteral::Create(Ctx, SVal, StringLiteral::Ascii, + /*Pascal*/ false, StrTy, Loc); + assert(Lit && "should not be null"); + return Lit; + }; + switch (getIdentType()) { + default: + llvm_unreachable("should not be here"); + case SourceLocExpr::File: + return CreateString(PLoc.getFilename()); + case SourceLocExpr::Function: { + DeclarationName Name; + if (const auto *FD = dyn_cast_or_null(CurContext)) + Name = FD->getDeclName(); + return CreateString(Name ? Name.getAsString() : ""); + } + } +} + +QualType SourceLocExpr::BuildSourceLocExprType(const ASTContext &Ctx, + IdentType Type, bool MakeArray, + int ArraySize) { + assert(MakeArray == (ArraySize != -1)); + bool IsIntType = Type == SourceLocExpr::Line || Type == SourceLocExpr::Column; + assert(!IsIntType || (IsIntType && !MakeArray)); + if (IsIntType) + return Ctx.UnsignedIntTy; + QualType Ty = Ctx.CharTy; + // A C++ string literal has a const-qualified element type (C++ 2.13.4p1). + if (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().ConstStrings) + Ty = Ty.withConst(); + if (!MakeArray) + return Ctx.getPointerType(Ty); + return Ctx.getConstantArrayType(Ty, llvm::APInt(32, ArraySize), + ArrayType::Normal, 0); +} + +Expr *SourceLocExpr::getValue(const ASTContext &Ctx, SourceLocation Loc, + const DeclContext *CurContext) const { + if (isLineOrColumn()) + return IntegerLiteral::Create(Ctx, getIntValue(Ctx, Loc), Ctx.UnsignedIntTy, + Loc); + return getStringValue(Ctx, Loc, CurContext); +} + InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc, ArrayRef initExprs, SourceLocation rbraceloc) : Expr(InitListExprClass, QualType(), VK_RValue, OK_Ordinary, false, false, @@ -3000,6 +3105,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 @@ -735,13 +735,14 @@ } CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &C, SourceLocation Loc, - FieldDecl *Field, QualType T) + FieldDecl *Field, QualType T, + DeclContext *UsedContext) : Expr(CXXDefaultInitExprClass, T.getNonLValueExprType(C), - T->isLValueReferenceType() ? VK_LValue : T->isRValueReferenceType() - ? VK_XValue - : VK_RValue, + T->isLValueReferenceType() + ? VK_LValue + : T->isRValueReferenceType() ? VK_XValue : VK_RValue, /*FIXME*/ OK_Ordinary, false, false, false, false), - Field(Field), Loc(Loc) { + Field(Field), UsedContext(UsedContext), Loc(Loc) { assert(Field->hasInClassInitializer()); } Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -190,6 +190,7 @@ case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: + case Expr::SourceLocExprClass: return Cl::CL_PRValue; // Next come the complicated cases. Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -40,6 +40,7 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/SourceLocExprScope.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" @@ -58,6 +59,7 @@ struct LValue; struct CallStackFrame; struct EvalInfo; + using SourceLocExprScope = SourceLocExprScopeBase; static QualType getType(APValue::LValueBase B) { if (!B) return QualType(); @@ -636,6 +638,14 @@ /// \brief Whether or not we're currently speculatively evaluating. bool IsSpeculativelyEvaluating; + /// \brief Source location information about the default argument expression + /// we're evaluating, if any. + SourceLocExprScope *CurCXXDefaultArgScope = nullptr; + + /// \brief Source location information about the default member initializer + /// we're evaluating, if any. + SourceLocExprScope *CurCXXDefaultInitScope = nullptr; + enum EvaluationMode { /// Evaluate as a constant expression. Stop if we find that the expression /// is not a constant expression. @@ -702,14 +712,14 @@ bool checkingForOverflow() { return EvalMode == EM_EvaluateForOverflow; } EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) - : Ctx(const_cast(C)), EvalStatus(S), CurrentCall(nullptr), - CallStackDepth(0), NextCallIndex(1), - StepsLeft(getLangOpts().ConstexprStepLimit), - BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), - EvaluatingDecl((const ValueDecl *)nullptr), - EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), - HasFoldFailureDiagnostic(false), IsSpeculativelyEvaluating(false), - EvalMode(Mode) {} + : Ctx(const_cast(C)), EvalStatus(S), CurrentCall(nullptr), + CallStackDepth(0), NextCallIndex(1), + StepsLeft(getLangOpts().ConstexprStepLimit), + BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), + EvaluatingDecl((const ValueDecl *)nullptr), + EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), + HasFoldFailureDiagnostic(false), IsSpeculativelyEvaluating(false), + EvalMode(Mode) {} void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { EvaluatingDecl = Base; @@ -4304,6 +4314,7 @@ const CXXConstructorDecl *Definition, EvalInfo &Info, APValue &Result) { SourceLocation CallLoc = E->getExprLoc(); + if (!Info.CheckCallLimit(CallLoc)) return false; @@ -4563,14 +4574,18 @@ { return StmtVisitorTy::Visit(E->getResultExpr()); } bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E) { return StmtVisitorTy::Visit(E->getReplacement()); } - bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) - { return StmtVisitorTy::Visit(E->getExpr()); } + bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { + SourceLocExprScope Guard(E, &Info.CurCXXDefaultArgScope, Info.CurrentCall); + return StmtVisitorTy::Visit(E->getExpr()); + } bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) { // The initializer may not have been parsed yet, or might be erroneous. if (!E->getExpr()) return Error(E); + SourceLocExprScope Guard(E, &Info.CurCXXDefaultInitScope, Info.CurrentCall); 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) @@ -5546,7 +5561,6 @@ bool visitNonBuiltinCallExpr(const CallExpr *E); public: - PointerExprEvaluator(EvalInfo &info, LValue &Result, bool InvalidBaseOK) : ExprEvaluatorBaseTy(info), Result(Result), InvalidBaseOK(InvalidBaseOK) {} @@ -5616,6 +5630,37 @@ return true; } + bool VisitSourceLocExpr(const SourceLocExpr *E) { + assert(E && !E->isLineOrColumn()); + Expr *Value = nullptr; + SourceLocExprScope *ArgCtx = Info.CurCXXDefaultInitScope; + if (!ArgCtx && Info.CurCXXDefaultArgScope && + Info.CurCXXDefaultArgScope->isInSameContext(Info.CurrentCall)) + ArgCtx = Info.CurCXXDefaultArgScope; + if (ArgCtx) { + Value = E->getValue(Info.Ctx, ArgCtx->getLoc(), ArgCtx->getContext()); + } else + Value = E->getValue(Info.Ctx); + + if (!Value) + return Error(E); + + LValue Obj; + assert(Value->isGLValue()); + + if (!evaluateLValue(Value, Result)) + return Error(E); + // The result is a pointer to the first element of the array. + if (const ConstantArrayType *CAT + = Info.Ctx.getAsConstantArrayType(Value->getType())) + Result.addArray(Info, Value, CAT); + else + Result.Designator.setInvalid(); + + assert(Result.Designator.Invalid == false); + return true; + } + // FIXME: Missing: @protocol, @selector }; } // end anonymous namespace @@ -6993,7 +7038,6 @@ //===--------------------------------------------------------------------===// // Visitor Methods //===--------------------------------------------------------------------===// - bool VisitIntegerLiteral(const IntegerLiteral *E) { return Success(E->getValue(), E); } @@ -7066,7 +7110,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 }; } // end anonymous namespace @@ -7098,7 +7142,18 @@ Result = Val.getInt(); return true; } - +bool IntExprEvaluator::VisitSourceLocExpr(const SourceLocExpr *E) { + assert(E && E->isLineOrColumn()); + llvm::APInt Result; + SourceLocation Loc = E->getLocStart(); + if (Info.CurCXXDefaultInitScope) + Loc = Info.CurCXXDefaultInitScope->getLoc(); + else if (Info.CurCXXDefaultArgScope && + Info.CurCXXDefaultArgScope->isInSameContext(Info.CurrentCall)) + Loc = Info.CurCXXDefaultArgScope->getLoc(); + Result = E->getIntValue(Info.Ctx, Loc); + return Success(Result, E); +} /// Check whether the given declaration can be directly converted to an integral /// rvalue. If not, no diagnostic is produced; there are other things we can /// try. @@ -10354,6 +10409,7 @@ case Expr::SizeOfPackExprClass: case Expr::GNUNullExprClass: + case Expr::SourceLocExprClass: // GCC considers the GNU __null value to be an integral constant expression. return NoDiag(); Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3471,7 +3471,7 @@ case Expr::AsTypeExprClass: case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: - { + case Expr::SourceLocExprClass: { if (!NullOut) { // As bad as this diagnostic is, it's better than crashing. DiagnosticsEngine &Diags = Context.getDiags(); Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -1306,6 +1306,10 @@ // Expr printing methods. //===----------------------------------------------------------------------===// +void StmtPrinter::VisitSourceLocExpr(SourceLocExpr *Node) { + OS << Node->getBuiltinStr() << "()"; +} + void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) { if (auto *OCED = dyn_cast(Node->getDecl())) { OCED->getInit()->IgnoreImpCasts()->printPretty(OS, nullptr, Policy); Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1818,6 +1818,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 @@ -1244,11 +1244,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 @@ -159,10 +159,11 @@ void VisitImplicitValueInitExpr(ImplicitValueInitExpr *E); void VisitNoInitExpr(NoInitExpr *E) { } // Do nothing. void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { + CodeGenFunction::CXXDefaultArgExprScope(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); @@ -183,18 +184,18 @@ } CGF.EmitPseudoObjectRValue(E, EnsureSlot(E->getType())); - } + } - void VisitVAArgExpr(VAArgExpr *E); + void VisitVAArgExpr(VAArgExpr * E); - void EmitInitializationToLValue(Expr *E, LValue Address); - void EmitNullInitializationToLValue(LValue Address); - // case Expr::ChooseExprClass: - void VisitCXXThrowExpr(const CXXThrowExpr *E) { CGF.EmitCXXThrowExpr(E); } - void VisitAtomicExpr(AtomicExpr *E) { - RValue Res = CGF.EmitAtomicExpr(E); - EmitFinalDestCopy(E->getType(), Res); - } + void EmitInitializationToLValue(Expr * E, LValue Address); + void EmitNullInitializationToLValue(LValue Address); + // case Expr::ChooseExprClass: + void VisitCXXThrowExpr(const CXXThrowExpr *E) { CGF.EmitCXXThrowExpr(E); } + void VisitAtomicExpr(AtomicExpr * E) { + RValue Res = CGF.EmitAtomicExpr(E); + EmitFinalDestCopy(E->getType(), Res); + } }; } // end anonymous namespace. Index: lib/CodeGen/CGExprComplex.cpp =================================================================== --- lib/CodeGen/CGExprComplex.cpp +++ lib/CodeGen/CGExprComplex.cpp @@ -210,10 +210,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/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -577,12 +577,31 @@ Value *VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) { return EmitLoadOfLValue(E); } + Value *VisitSourceLocExpr(SourceLocExpr *SLE) { + auto &Ctx = CGF.getContext(); + SourceLocation Loc = SLE->getLocation(); + const DeclContext *DC = SLE->getParentContext(); + if (auto *S = (CGF.CurCXXDefaultInitScope ? CGF.CurCXXDefaultInitScope + : CGF.CurCXXDefaultArgScope)) { + Loc = S->getLoc(); + DC = S->getContext(); + } + if (SLE->isLineOrColumn()) { + auto Val = SLE->getIntValue(Ctx, Loc); + return Builder.getInt(Val); + } else { + StringLiteral *Str = SLE->getStringValue(Ctx, Loc, DC); + return CGF.EmitArrayToPointerDecay(Str).getPointer(); + } + llvm_unreachable("missing return"); + } 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 @@ -26,6 +26,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" +#include "clang/AST/SourceLocExprScope.h" #include "clang/AST/Type.h" #include "clang/Basic/ABI.h" #include "clang/Basic/CapturedStmt.h" @@ -96,6 +97,7 @@ class TargetCodeGenInfo; struct OMPTaskDataTy; struct CGCoroData; +using SourceLocExprScope = SourceLocExprScopeBase; /// The kind of evaluation to perform on values of a particular /// type. Basically, is the code in CGExprScalar, CGExprComplex, or @@ -1243,6 +1245,15 @@ SourceLocation LastStopPoint; public: + + /// \brief Source location information about the default argument expression + /// we're evaluating, if any. + SourceLocExprScope *CurCXXDefaultArgScope = nullptr; + + /// \brief Source location information about the default member initializer + /// we're evaluating, if any. + SourceLocExprScope *CurCXXDefaultInitScope = nullptr; + /// 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. @@ -1263,11 +1274,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.CurCXXDefaultInitScope, CGF.CurCodeDecl) { CGF.CXXThisValue = CGF.CXXDefaultInitExprThis.getPointer(); CGF.CXXThisAlignment = CGF.CXXDefaultInitExprThis.getAlignment(); } @@ -1280,6 +1292,16 @@ CodeGenFunction &CGF; llvm::Value *OldCXXThisValue; CharUnits OldCXXThisAlignment; + SourceLocExprScope SourceLocScope; + }; + + /// The scope of a CXXDefaultArgExpr. + class CXXDefaultArgExprScope : public SourceLocExprScope { + using Base = SourceLocExprScope; + + public: + CXXDefaultArgExprScope(CodeGenFunction &CGF, const CXXDefaultArgExpr *E) + : Base(E, &CGF.CurCXXDefaultArgScope, CGF.CurCodeDecl) {} }; /// 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 @@ -625,6 +625,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 ']' @@ -1080,6 +1084,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()); @@ -2000,6 +2008,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: @@ -2219,6 +2231,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::IdentType Type = [&] { + 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(Type, 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); @@ -12550,7 +12550,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()) @@ -12591,7 +12591,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 @@ -1303,6 +1303,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 @@ -4591,7 +4591,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 @@ -4849,8 +4849,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; @@ -13244,6 +13243,22 @@ return new (Context) GNUNullExpr(Ty, TokenLoc); } +ExprResult Sema::ActOnSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + return BuildSourceLocExpr(Type, BuiltinLoc, RPLoc, CurContext); +} + +ExprResult Sema::BuildSourceLocExpr(SourceLocExpr::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, + DeclContext *ParentContext) { + + return new (Context) SourceLocExpr( + Type, BuiltinLoc, RPLoc, + SourceLocExpr::BuildSourceLocExprType(Context, Type), ParentContext); +} + bool Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp, bool Diagnose) { if (!getLangOpts().ObjC1) Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -2663,9 +2663,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); } /// \brief Build a new C++11 default-initialization expression. @@ -2675,7 +2675,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); } /// \brief Build a new C++ zero-initialization expression. @@ -2929,6 +2930,18 @@ RParenLoc, Length, PartialArgs); } + /// \brief 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::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, + DeclContext *ParentContext) { + return getSema().BuildSourceLocExpr(Type, BuiltinLoc, RPLoc, ParentContext); + } + /// \brief Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. @@ -9832,6 +9845,20 @@ return getDerived().TransformCallExpr(E); } +template +ExprResult TreeTransform::TransformSourceLocExpr(SourceLocExpr *E) { + bool NeedRebuildFunc = E->getIdentType() == SourceLocExpr::Function && + isa(getSema().CurContext) && + getSema().CurContext != E->getParentContext(); + + if (!getDerived().AlwaysRebuild() && !NeedRebuildFunc) + return E; + + return getDerived().RebuildSourceLocExpr(E->getIdentType(), E->getLocStart(), + E->getLocEnd(), + getSema().CurContext); +} + template ExprResult TreeTransform::TransformCUDAKernelCallExpr(CUDAKernelCallExpr *E) { @@ -10067,8 +10094,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); @@ -10083,7 +10110,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 @@ -863,6 +863,14 @@ E->setIsMicrosoftABI(Record.readInt()); } +void ASTStmtReader::VisitSourceLocExpr(SourceLocExpr *E) { + VisitExpr(E); + E->setParentContext(ReadDeclAs()); + E->setLocStart(ReadSourceLocation()); + E->setLocEnd(ReadSourceLocation()); + E->setIdentType(static_cast(Record.readInt())); +} + void ASTStmtReader::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); E->setAmpAmpLoc(ReadSourceLocation()); @@ -1377,12 +1385,14 @@ void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { VisitExpr(E); E->Param = ReadDeclAs(); + E->UsedContext = ReadDeclAs(); E->Loc = ReadSourceLocation(); } void ASTStmtReader::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) { VisitExpr(E); E->Field = ReadDeclAs(); + E->UsedContext = ReadDeclAs(); E->Loc = ReadSourceLocation(); } @@ -3348,6 +3358,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 @@ -844,6 +844,15 @@ Code = serialization::EXPR_VA_ARG; } +void ASTStmtWriter::VisitSourceLocExpr(SourceLocExpr *E) { + VisitExpr(E); + Record.AddDeclRef(cast_or_null(E->getParentContext())); + Record.AddSourceLocation(E->getLocStart()); + Record.AddSourceLocation(E->getLocEnd()); + Record.push_back(E->getIdentType()); + Code = serialization::EXPR_SOURCE_LOC; +} + void ASTStmtWriter::VisitAddrLabelExpr(AddrLabelExpr *E) { VisitExpr(E); Record.AddSourceLocation(E->getAmpAmpLoc()); @@ -1387,6 +1396,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; } @@ -1394,6 +1404,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 @@ -1095,6 +1095,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_FUNCTION.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/builtin_FUNCTION.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -std=c++1z -fblocks %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %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(); +} + +template +void test_template(const char *f = __builtin_FUNCTION()) { + (void)__builtin_FUNCTION(); +} +void do_template_test() { + test_template(); +} +} // namespace test_func Index: test/CodeGenCXX/builtin_LINE.cpp =================================================================== --- /dev/null +++ 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 @@ -291,6 +291,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 =================================================================== --- /dev/null +++ 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 =================================================================== --- /dev/null +++ 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 =================================================================== --- /dev/null +++ test/SemaCXX/Inputs/source-location-file.h @@ -0,0 +1,43 @@ + +// 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 =================================================================== --- /dev/null +++ test/SemaCXX/source_location.cpp @@ -0,0 +1,475 @@ +// 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__ + +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 313 + using SLF::TestClass; + constexpr TestClass Default; + constexpr TestClass InParam{42}; + constexpr TestClass Template{42, 42}; + 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