Index: docs/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -2172,6 +2172,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/APValue.h =================================================================== --- include/clang/AST/APValue.h +++ include/clang/AST/APValue.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_APVALUE_H #define LLVM_CLANG_AST_APVALUE_H +#include "clang/AST/EvaluatedSourceLocScope.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" @@ -24,12 +25,14 @@ class AddrLabelExpr; class ASTContext; class CharUnits; + class DeclContext; class DiagnosticBuilder; class Expr; class FieldDecl; class Decl; class ValueDecl; class CXXRecordDecl; + class SourceLocation; class QualType; /// APValue - This class implements a discriminated union of [uninitialized] @@ -58,11 +61,13 @@ public: typedef llvm::PointerUnion PtrTy; + LValueBase() : CallIndex(0), Version(0) {} template - LValueBase(T P, unsigned I = 0, unsigned V = 0) - : Ptr(P), CallIndex(I), Version(V) {} + LValueBase(T P, unsigned I = 0, unsigned V = 0, + EvaluatedSourceLocScopeBase BaseScope = {}) + : Ptr(P), CallIndex(I), Version(V), LocContext(BaseScope) {} template bool is() const { return Ptr.is(); } @@ -73,6 +78,14 @@ template T dyn_cast() const { return Ptr.dyn_cast(); } + bool hasEvaluatedSourceLocScope() const { return bool(LocContext); } + EvaluatedSourceLocScopeBase getEvaluatedSourceLocScope() const { + return LocContext; + } + void setEvaluatedSourceLocScope(EvaluatedSourceLocScopeBase Other) { + LocContext = Other; + } + void *getOpaqueValue() const; bool isNull() const; @@ -97,12 +110,13 @@ bool operator==(const LValueBase &Other) const { return Ptr == Other.Ptr && CallIndex == Other.CallIndex && - Version == Other.Version; + Version == Other.Version && LocContext == Other.LocContext; } private: PtrTy Ptr; unsigned CallIndex, Version; + EvaluatedSourceLocScopeBase LocContext; }; typedef llvm::PointerIntPair BaseOrMemberType; Index: include/clang/AST/EvaluatedSourceLocScope.h =================================================================== --- /dev/null +++ include/clang/AST/EvaluatedSourceLocScope.h @@ -0,0 +1,81 @@ +//===--- EvaluatedSourceLocScope.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 EvaluatedSourceLocScopeBase used to track the current +// context and location when evaluating source location builtins +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_EVALUATED_SOURCE_LOC_SCOPE_H +#define LLVM_CLANG_AST_EVALUATED_SOURCE_LOC_SCOPE_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseMapInfo.h" + +namespace clang { +class ASTContext; +class DeclContext; +class Expr; +class SourceLocExpr; +class SourceLocation; +class QualType; + +struct EvaluatedSourceLocScopeBase { +private: + friend struct llvm::DenseMapInfo; + + const void *Type = nullptr; + const void *Loc = nullptr; + const DeclContext *Context = nullptr; + + EvaluatedSourceLocScopeBase(const void *Ty, const void *L, + const DeclContext *Ctx) + : Type(Ty), Loc(L), Context(Ctx) {} + +public: + EvaluatedSourceLocScopeBase() = default; + EvaluatedSourceLocScopeBase(QualType const &Ty, SourceLocation const &Loc, + const DeclContext *Ctx); + + static EvaluatedSourceLocScopeBase Create(const ASTContext &Ctx, + const SourceLocExpr *E, + const Expr *DefaultExpr); + + bool empty() const { return Type == nullptr; } + explicit operator bool() const { return !empty(); } + + QualType getType() const; + void setType(const QualType &T); + + SourceLocation getLocation() const; + void setLocation(const SourceLocation &L); + + const DeclContext *getContext() const { return Context; } + void setContext(const DeclContext *C) { Context = C; } + + friend inline bool operator==(EvaluatedSourceLocScopeBase const &LHS, + EvaluatedSourceLocScopeBase const &RHS) { + return LHS.Type == RHS.Type && LHS.Loc == RHS.Loc && + LHS.Context == RHS.Context; + } +}; + +} // end namespace clang + +namespace llvm { +template <> struct DenseMapInfo { + using KeyType = clang::EvaluatedSourceLocScopeBase; + static KeyType getEmptyKey(); + static KeyType getTombstoneKey(); + static unsigned getHashValue(const KeyType &Val); + static bool isEqual(const KeyType &LHS, const KeyType &RHS); +}; +} // namespace llvm + +#endif // LLVM_CLANG_AST_EVALUATED_SOURCE_LOC_SCOPE_H Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -3904,6 +3904,78 @@ } }; +/// 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, IT_End = Column }; + +private: + SourceLocation BuiltinLoc, RParenLoc; + DeclContext *ParentContext; + + static QualType BuildDependentSourceLocExprType(const ASTContext &Ctx, + IdentType Type); + +public: + static QualType BuildStringArrayType(const ASTContext &Ctx, unsigned Size); + + SourceLocExpr(const ASTContext &Ctx, IdentType Type, SourceLocation BLoc, + SourceLocation RParenLoc, DeclContext *Context); + + /// Build an empty call expression. + explicit SourceLocExpr(EmptyShell Empty) : Expr(SourceLocExprClass, Empty) {} + + /// 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 isStringType() const LLVM_READONLY { + switch (getIdentType()) { + 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 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; } +}; + + /// 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,10 +1043,14 @@ /// The parameter whose default is being used. ParmVarDecl *Param; + /// The context where the default argument expression was used. + DeclContext *UsedContext; + /// 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() @@ -1054,7 +1058,7 @@ 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,9 @@ 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 Loc; } @@ -1114,11 +1123,14 @@ /// The field whose default is being used. FieldDecl *Field; + /// The context where the default initializer expression was used. + DeclContext *UsedContext; + /// 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 +1141,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); } /// Get the field whose initializer will be used. @@ -1147,6 +1161,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 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,164 @@ +//===--- 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/EvaluatedSourceLocScope.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceLocation.h" +#include +#include +#include + +namespace clang { +class ASTContext; +class Expr; +class DeclContext; +class SourceLocExpr; + +class SourceLocExprScopeGuard; + +struct EvaluatedSourceLocScope : public EvaluatedSourceLocScopeBase { +private: + std::string SValue; + +public: + const SourceLocExpr *E = nullptr; + APValue Result; + + std::string const &getStringValue() const { + assert(E->isStringType() && + "cannot set string value for non-string expression"); + return SValue; + } + void setStringValue(std::string Other) { + assert(E->isStringType() && + "cannot set string value for non-string expression"); + SValue = std::move(Other); + } + + uint32_t getIntValue() const { + assert(E->isIntType() && "cannot get int value from non-int expression"); + assert(Result.isInt() && "non-int APValue result"); + return Result.getInt().getZExtValue(); + } + + EvaluatedSourceLocScope() = default; + EvaluatedSourceLocScope(EvaluatedSourceLocScope const &) = default; + + static EvaluatedSourceLocScope Create(const ASTContext &Ctx, + const SourceLocExpr *E, + EvaluatedSourceLocScopeBase Base); + + static EvaluatedSourceLocScope Create(const ASTContext &Ctx, + const SourceLocExpr *E, + const Expr *DefaultExpr) { + return Create(Ctx, E, + EvaluatedSourceLocScopeBase::Create(Ctx, E, DefaultExpr)); + } + +private: + EvaluatedSourceLocScope(EvaluatedSourceLocScopeBase Base, + const SourceLocExpr *E) + : EvaluatedSourceLocScopeBase(Base), E(E) {} + + friend class CurrentSourceLocExprScope; +}; + +/// 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 { + friend class SourceLocExprScopeGuard; + + /// The CXXDefaultArgExpr or CXXDefaultInitExpr we're currently evaluating. + const Expr *DefaultExpr = nullptr; + + /// A pointer representing the current evaluation context. It is used to + /// determine context equality. + // FIXME: using 'void*' is a type-unsafe hack used to avoid making the type + // a class template. + const void *EvalContextID = nullptr; + +public: + CurrentSourceLocExprScope() = default; + CurrentSourceLocExprScope(const Expr *DefaultExpr, const void *EvalContextID); + + EvaluatedSourceLocScope getEvaluatedInfo(const ASTContext &Ctx, + const SourceLocExpr *E) const { + return EvaluatedSourceLocScope::Create(Ctx, E, DefaultExpr); + } + + bool empty() const { return DefaultExpr == nullptr; } + explicit operator bool() const { return !empty(); } + + const Expr *getDefaultExpr() const { return DefaultExpr; } + + bool isInSameContext(CurrentSourceLocExprScope const &Other) const { + return EvalContextID == Other.EvalContextID; + } + + template + bool isInSameContext(EvalContextType *CurEvalContext) const { + static_assert(!std::is_same::type, + CurrentSourceLocExprScope>::value, + "incorrect overload selected"); + return EvalContextID == static_cast(CurEvalContext); + } + +private: + CurrentSourceLocExprScope(CurrentSourceLocExprScope const &) = default; + CurrentSourceLocExprScope & + operator=(CurrentSourceLocExprScope const &) = default; +}; + +/// A RAII style scope gaurd used for updating and tracking the current source +/// location and context as used by the source location builtins +/// (ex. __builtin_LINE). +class SourceLocExprScopeGuard { + static bool ShouldEnable(CurrentSourceLocExprScope const &CurrentScope, + CurrentSourceLocExprScope const &NewScope); + + SourceLocExprScopeGuard(CurrentSourceLocExprScope NewScope, + CurrentSourceLocExprScope &Current); + +public: + template + SourceLocExprScopeGuard(const Expr *DefaultExpr, + CurrentSourceLocExprScope &Current, + EvalContextType *EvalContext) + : SourceLocExprScopeGuard( + CurrentSourceLocExprScope(DefaultExpr, EvalContext), Current) { + static_assert(!std::is_same::type, + CurrentSourceLocExprScope>::value, + "incorrect overload selected"); + } + + ~SourceLocExprScopeGuard(); + +private: + SourceLocExprScopeGuard(SourceLocExprScopeGuard const &) = delete; + SourceLocExprScopeGuard &operator=(SourceLocExprScopeGuard const &) = delete; + + CurrentSourceLocExprScope &Current; + CurrentSourceLocExprScope OldVal; + 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 @@ -291,6 +291,16 @@ unsigned IsImplicit : 1; }; + class SourceLocExprBitfields { + friend class SourceLocExpr; + + unsigned : NumExprBits; + + /// The type of source location builtin represented by the SourceLocExpr. + /// Ex. __builtin_LINE, __builtin_FUNCTION, ect. + unsigned Type : 2; + }; + union { StmtBitfields StmtBits; CompoundStmtBitfields CompoundStmtBits; @@ -309,6 +319,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 @@ -92,6 +92,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 @@ -403,6 +403,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); + + /// 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 @@ -1712,6 +1712,9 @@ /// A GNUNullExpr record. EXPR_GNU_NULL, + /// A SourceLocExpr record. + EXPR_SOURCE_LOC, + /// A ShuffleVectorExpr record. EXPR_SHUFFLE_VECTOR, Index: lib/AST/APValue.cpp =================================================================== --- lib/AST/APValue.cpp +++ lib/AST/APValue.cpp @@ -43,12 +43,14 @@ return static_cast(Ptr); } + clang::APValue::LValueBase llvm::DenseMapInfo::getEmptyKey() { return clang::APValue::LValueBase( DenseMapInfo::getEmptyKey(), DenseMapInfo::getEmptyKey(), - DenseMapInfo::getEmptyKey()); + DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()); } clang::APValue::LValueBase @@ -56,7 +58,8 @@ return clang::APValue::LValueBase( DenseMapInfo::getTombstoneKey(), DenseMapInfo::getTombstoneKey(), - DenseMapInfo::getTombstoneKey()); + DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()); } unsigned llvm::DenseMapInfo::getHashValue( @@ -65,6 +68,8 @@ ID.AddPointer(Base.getOpaqueValue()); ID.AddInteger(Base.getCallIndex()); ID.AddInteger(Base.getVersion()); + ID.AddInteger(DenseMapInfo::getHashValue( + Base.getEvaluatedSourceLocScope())); return ID.ComputeHash(); } @@ -499,7 +504,14 @@ } else { // The lvalue must refer to an array. Out << '[' << Path[I].ArrayIndex << ']'; + // FIXME: Unless the source expression is a SourceLocExpr, then the + // type refers to a decayed pointer. + if (ElemTy->isArrayType()) ElemTy = Ctx.getAsArrayType(ElemTy)->getElementType(); + else { + assert(ElemTy->isPointerType()); + ElemTy = ElemTy->getPointeeType(); + } } } Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -5868,8 +5868,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) { @@ -6537,8 +6542,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/CMakeLists.txt =================================================================== --- lib/AST/CMakeLists.txt +++ lib/AST/CMakeLists.txt @@ -55,6 +55,7 @@ RecordLayout.cpp RecordLayoutBuilder.cpp SelectorLocationsKind.cpp + SourceLocExprScope.cpp Stmt.cpp StmtCXX.cpp StmtIterator.cpp Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1862,6 +1862,7 @@ return OverOps[Opc]; } + bool BinaryOperator::isNullPointerArithmeticExtension(ASTContext &Ctx, Opcode Opc, Expr *LHS, Expr *RHS) { @@ -1894,6 +1895,58 @@ return true; } + +SourceLocExpr::SourceLocExpr(const ASTContext &Ctx, IdentType Type, + SourceLocation BLoc, SourceLocation RParenLoc, + DeclContext *ParentContext) + : Expr(SourceLocExprClass, BuildDependentSourceLocExprType(Ctx, Type), + VK_RValue, OK_Ordinary, false, false, false, false), + BuiltinLoc(BLoc), RParenLoc(RParenLoc), ParentContext(ParentContext) { + SourceLocExprBits.Type = Type; +} + +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"; + } +} + +QualType SourceLocExpr::BuildDependentSourceLocExprType(const ASTContext &Ctx, + IdentType Type) { + switch (Type) { + case File: + case Function: { + 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(); + return Ctx.getPointerType(Ty); + } + case Line: + case Column: + return Ctx.UnsignedIntTy; + } + llvm_unreachable("unhandled case"); +} + +QualType SourceLocExpr::BuildStringArrayType(const ASTContext &Ctx, + unsigned Size) { + 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(); + + return Ctx.getConstantArrayType(Ty, llvm::APInt(32, Size), ArrayType::Normal, + 0); +} + InitListExpr::InitListExpr(const ASTContext &C, SourceLocation lbraceloc, ArrayRef initExprs, SourceLocation rbraceloc) : Expr(InitListExprClass, QualType(), VK_RValue, OK_Ordinary, false, false, @@ -3048,6 +3101,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 @@ -734,13 +734,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 @@ -192,6 +192,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" @@ -63,6 +64,10 @@ static QualType getType(APValue::LValueBase B) { if (!B) return QualType(); + + if (B.hasEvaluatedSourceLocScope()) + return B.getEvaluatedSourceLocScope().getType(); + if (const ValueDecl *D = B.dyn_cast()) { // FIXME: It's unclear where we're supposed to take the type from, and // this actually matters for arrays of unknown bound. Eg: @@ -112,12 +117,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(getAsBaseOrMember(E).getPointer()); + return dyn_cast_or_null(getAsBaseOrMember(E).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(getAsBaseOrMember(E).getPointer()); + return dyn_cast_or_null(getAsBaseOrMember(E).getPointer()); } /// Determine whether this LValue path entry for a base class names a virtual /// base class. @@ -693,6 +698,10 @@ /// Whether or not we're currently speculatively evaluating. bool IsSpeculativelyEvaluating; + /// Source location information about the default argument or default + /// initializer expression we're evaluating, if any. + CurrentSourceLocExprScope CurSourceLocExprScope; + enum EvaluationMode { /// Evaluate as a constant expression. Stop if we find that the expression /// is not a constant expression. @@ -1686,6 +1695,10 @@ case Expr::CXXTypeidExprClass: case Expr::CXXUuidofExprClass: return true; + + case Expr::SourceLocExprClass: + return cast(E)->isStringType(); + case Expr::CallExprClass: return IsStringLiteralCall(cast(E)); // For GCC compatibility, &&label has static storage duration. @@ -2579,8 +2592,10 @@ } /// Extract the value of a character from a string literal. -static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit, +static APSInt extractStringLiteralCharacter(EvalInfo &Info, + APValue::LValueBase const &Base, uint64_t Index) { + const Expr *Lit = Base.get(); // FIXME: Support MakeStringConstant if (const auto *ObjCEnc = dyn_cast(Lit)) { std::string Str; @@ -2589,6 +2604,26 @@ return APSInt::getUnsigned(Str.c_str()[Index]); } + if (const auto *SLE = dyn_cast(Lit)) { + assert(Base.hasEvaluatedSourceLocScope()); + + auto FullInfo = EvaluatedSourceLocScope::Create( + Info.Ctx, SLE, Base.getEvaluatedSourceLocScope()); + + std::string const &Str = FullInfo.getStringValue(); + + const auto *StrTy = Info.Ctx.getAsConstantArrayType(FullInfo.getType()); + assert(StrTy && (StrTy->getSize().getZExtValue() == Str.size() + 1)); + + auto Width = Info.Ctx.getCharWidth(); + APSInt Res(Width, StrTy->getElementType()->isUnsignedIntegerType()); + if (Index <= Str.size()) { + llvm::APInt IntVal(Width, Str.c_str()[Index]); + Res = IntVal; + } + return Res; + } + if (auto PE = dyn_cast(Lit)) Lit = PE->getFunctionName(); const StringLiteral *S = cast(Lit); @@ -2771,6 +2806,7 @@ } if (I == N) { + assert(!ObjType.isNull()); // If we are reading an object of class type, there may still be more // things we need to check: if there are any mutable subobjects, we // cannot perform this read. (This only happens when performing a trivial @@ -2813,6 +2849,7 @@ // An array object is represented as either an Array APValue or as an // LValue which refers to a string literal. if (O->isLValue()) { + assert(!isa(E)); assert(I == N - 1 && "extracting subobject of character?"); assert(!O->hasLValuePath() || O->getLValuePath().empty()); if (handler.AccessKind != AK_Read) @@ -2935,8 +2972,8 @@ return true; } bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) { - Result = APValue(extractStringLiteralCharacter( - Info, Subobj.getLValueBase().get(), Character)); + Result = APValue( + extractStringLiteralCharacter(Info, Subobj.getLValueBase(), Character)); return true; } }; @@ -3318,6 +3355,15 @@ APValue Str(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0); CompleteObject StrObj(&Str, Base->getType(), false); return extractSubobject(Info, Conv, StrObj, LVal.Designator, RVal); + } else if (const SourceLocExpr *SLE = dyn_cast(Base)) { + // APValue Str(LVal.Base, CharUnits::Zero(), APValue::NoLValuePath(), 0); + assert(LVal.Base.hasEvaluatedSourceLocScope() && + "the type of a SourceLocExpr must be explicitly specified"); + EvaluatedSourceLocScope LocScope = EvaluatedSourceLocScope::Create( + Info.Ctx, SLE, LVal.Base.getEvaluatedSourceLocScope()); + + CompleteObject StrObj(&LocScope.Result, LocScope.getType(), false); + return extractSubobject(Info, Conv, StrObj, LVal.Designator, RVal); } } @@ -4415,6 +4461,7 @@ const CXXConstructorDecl *Definition, EvalInfo &Info, APValue &Result) { SourceLocation CallLoc = E->getExprLoc(); + if (!Info.CheckCallLimit(CallLoc)) return false; @@ -4689,6 +4736,8 @@ { return StmtVisitorTy::Visit(E->getReplacement()); } bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { TempVersionRAII RAII(*Info.CurrentCall); + SourceLocExprScopeGuard Guard(E, Info.CurSourceLocExprScope, + Info.CurrentCall); return StmtVisitorTy::Visit(E->getExpr()); } bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) { @@ -4696,8 +4745,11 @@ // The initializer may not have been parsed yet, or might be erroneous. if (!E->getExpr()) return Error(E); + SourceLocExprScopeGuard Guard(E, Info.CurSourceLocExprScope, + 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) @@ -5678,7 +5730,6 @@ bool visitNonBuiltinCallExpr(const CallExpr *E); public: - PointerExprEvaluator(EvalInfo &info, LValue &Result, bool InvalidBaseOK) : ExprEvaluatorBaseTy(info), Result(Result), InvalidBaseOK(InvalidBaseOK) {} @@ -5748,6 +5799,16 @@ return true; } + bool VisitSourceLocExpr(const SourceLocExpr *E) { + assert(E && E->isStringType()); + EvaluatedSourceLocScope LocScope = + Info.CurSourceLocExprScope.getEvaluatedInfo(Info.Ctx, E); + Result.set(LocScope.Result.getLValueBase()); + Result.addArray(Info, E, + Info.Ctx.getAsConstantArrayType(LocScope.getType())); + return true; + } + // FIXME: Missing: @protocol, @selector }; } // end anonymous namespace @@ -7096,6 +7157,7 @@ "Invalid evaluation result."); assert(SI.getBitWidth() == Info.Ctx.getIntWidth(E->getType()) && "Invalid evaluation result."); + Result = APValue(SI); return true; } @@ -7144,7 +7206,6 @@ //===--------------------------------------------------------------------===// // Visitor Methods //===--------------------------------------------------------------------===// - bool VisitIntegerLiteral(const IntegerLiteral *E) { return Success(E->getValue(), E); } @@ -7217,7 +7278,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 }; @@ -7317,6 +7378,13 @@ return true; } +bool IntExprEvaluator::VisitSourceLocExpr(const SourceLocExpr *E) { + assert(E && E->isIntType()); + EvaluatedSourceLocScope LocScope = + Info.CurSourceLocExprScope.getEvaluatedInfo(Info.Ctx, E); + return Success(LocScope.getIntValue(), 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. @@ -10847,6 +10915,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 @@ -3501,6 +3501,7 @@ case Expr::AsTypeExprClass: case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: + case Expr::SourceLocExprClass: case Expr::FixedPointLiteralClass: { if (!NullOut) { Index: lib/AST/SourceLocExprScope.cpp =================================================================== --- /dev/null +++ lib/AST/SourceLocExprScope.cpp @@ -0,0 +1,203 @@ +//===--- SourceLocExprScope.cpp - Mangle C++ Names --------------------------*- +// 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. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/SourceLocExprScope.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/Basic/SourceLocation.h" + +using namespace clang; + +QualType EvaluatedSourceLocScopeBase::getType() const { + if (!Type) + return QualType(); + return QualType::getFromOpaquePtr(Type); +} + +void EvaluatedSourceLocScopeBase::setType(const QualType &T) { + Type = T.getAsOpaquePtr(); +} + +SourceLocation EvaluatedSourceLocScopeBase::getLocation() const { + if (!Loc) + return SourceLocation(); + return SourceLocation::getFromPtrEncoding(Loc); +} + +void EvaluatedSourceLocScopeBase::setLocation(const SourceLocation &L) { + Loc = L.getPtrEncoding(); +} + +EvaluatedSourceLocScopeBase::EvaluatedSourceLocScopeBase( + QualType const &Ty, SourceLocation const &L, const DeclContext *Ctx) + : Type(Ty.getAsOpaquePtr()), Loc(L.getPtrEncoding()), Context(Ctx) {} + +EvaluatedSourceLocScopeBase +llvm::DenseMapInfo::getEmptyKey() { + return EvaluatedSourceLocScopeBase( + DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()); +} + +EvaluatedSourceLocScopeBase +llvm::DenseMapInfo::getTombstoneKey() { + return EvaluatedSourceLocScopeBase( + DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()); +} + +unsigned llvm::DenseMapInfo::getHashValue( + EvaluatedSourceLocScopeBase const &Val) { + llvm::FoldingSetNodeID ID; + ID.AddPointer(Val.Type); + ID.AddPointer(Val.Loc); + ID.AddPointer(Val.Context); + return ID.ComputeHash(); +} + +bool llvm::DenseMapInfo::isEqual( + EvaluatedSourceLocScopeBase const &LHS, + EvaluatedSourceLocScopeBase const &RHS) { + return LHS == RHS; +} + +CurrentSourceLocExprScope::CurrentSourceLocExprScope(const Expr *DefaultExpr, + const void *EvalContextID) + : DefaultExpr(DefaultExpr), EvalContextID(EvalContextID) { + assert(DefaultExpr && "expression cannot be null"); + assert(EvalContextID && "context pointer cannot be null"); + assert((isa(DefaultExpr) || + isa(DefaultExpr)) && + "expression must be either a default argument or initializer"); +} + +static PresumedLoc getPresumedSourceLoc(const ASTContext &Ctx, + SourceLocation Loc) { + auto &SourceMgr = Ctx.getSourceManager(); + PresumedLoc PLoc = + SourceMgr.getPresumedLoc(SourceMgr.getExpansionRange(Loc).getEnd()); + assert(PLoc.isValid()); // FIXME: Learn how to handle this. + return PLoc; +} + +static std::string getStringValue(const ASTContext &Ctx, const SourceLocExpr *E, + SourceLocation Loc, + const DeclContext *Context) { + switch (E->getIdentType()) { + case SourceLocExpr::File: + return getPresumedSourceLoc(Ctx, Loc).getFilename(); + case SourceLocExpr::Function: + if (const auto *FD = dyn_cast_or_null(Context)) { + if (DeclarationName Name = FD->getDeclName()) + return Name.getAsString(); + } + return ""; + case SourceLocExpr::Line: + case SourceLocExpr::Column: + llvm_unreachable("no string value for __builtin_LINE\\COLUMN"); + } +} + +EvaluatedSourceLocScopeBase EvaluatedSourceLocScopeBase::Create( + const ASTContext &Ctx, const SourceLocExpr *E, const Expr *DefaultExpr) { + EvaluatedSourceLocScopeBase Base; + + if (auto *DIE = dyn_cast_or_null(DefaultExpr)) { + Base.setLocation(DIE->getUsedLocation()); + Base.setContext(DIE->getUsedContext()); + } else if (auto *DAE = dyn_cast_or_null(DefaultExpr)) { + Base.setLocation(DAE->getUsedLocation()); + Base.setContext(DAE->getUsedContext()); + } else { + Base.setLocation(E->getLocation()); + Base.setContext(E->getParentContext()); + } + + if (E->isStringType()) + Base.setType(SourceLocExpr::BuildStringArrayType( + Ctx, + getStringValue(Ctx, E, Base.getLocation(), Base.getContext()).size() + + 1)); + else + Base.setType(Ctx.UnsignedIntTy); + + return Base; +} + +EvaluatedSourceLocScope +EvaluatedSourceLocScope::Create(ASTContext const &Ctx, const SourceLocExpr *E, + EvaluatedSourceLocScopeBase Base) { + EvaluatedSourceLocScope Info{Base, E}; + + PresumedLoc PLoc = getPresumedSourceLoc(Ctx, Info.getLocation()); + assert(PLoc.isValid()); + + switch (E->getIdentType()) { + case SourceLocExpr::File: + case SourceLocExpr::Function: { + std::string Val = + ::getStringValue(Ctx, E, Info.getLocation(), Info.getContext()); + Info.setStringValue(std::move(Val)); + + APValue::LValueBase LVBase(E); + LVBase.setEvaluatedSourceLocScope(Base); + APValue StrVal(LVBase, CharUnits::Zero(), APValue::NoLValuePath{}); + Info.Result.swap(StrVal); + } break; + case SourceLocExpr::Line: + case SourceLocExpr::Column: { + auto Val = E->getIdentType() == SourceLocExpr::Line ? PLoc.getLine() + : PLoc.getColumn(); + + llvm::APSInt TmpRes(llvm::APInt(Ctx.getTargetInfo().getIntWidth(), Val)); + APValue NewVal(TmpRes); + Info.Result.swap(NewVal); + } break; + } + return Info; +} + +SourceLocExprScopeGuard::SourceLocExprScopeGuard( + CurrentSourceLocExprScope NewScope, CurrentSourceLocExprScope &Current) + : Current(Current), OldVal(Current), Enable(false) { + if ((Enable = ShouldEnable(Current, NewScope))) + Current = NewScope; +} + +bool SourceLocExprScopeGuard::ShouldEnable( + CurrentSourceLocExprScope const &CurrentScope, + CurrentSourceLocExprScope const &NewScope) { + assert(!NewScope.empty() && "the new scope should not be empty"); + // 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__); + if (CurrentScope.empty()) + return true; + // if (isa(CurrentScope.DefaultExpr) && + // isa(NewScope.DefaultExpr)) + // return false; + return !CurrentScope.isInSameContext(NewScope); +} + +SourceLocExprScopeGuard::~SourceLocExprScopeGuard() { + if (Enable) + Current = OldVal; +} Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -1339,6 +1339,10 @@ // Expr printing methods. //===----------------------------------------------------------------------===// +void StmtPrinter::VisitSourceLocExpr(SourceLocExpr *Node) { + OS << Node->getBuiltinStr() << "()"; +} + void StmtPrinter::VisitDeclRefExpr(DeclRefExpr *Node) { if (const 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 @@ -1850,6 +1850,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 @@ -1237,6 +1237,8 @@ return EmitStringLiteralLValue(cast(E)); case Expr::ObjCEncodeExprClass: return EmitObjCEncodeExprLValue(cast(E)); + case Expr::SourceLocExprClass: + return EmitSourceLocExprLValue(cast(E)); case Expr::PseudoObjectExprClass: return EmitPseudoObjectLValue(cast(E)); case Expr::InitListExprClass: @@ -1269,11 +1271,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)); @@ -2625,6 +2631,14 @@ E->getType(), AlignmentSource::Decl); } +LValue CodeGenFunction::EmitSourceLocExprLValue(const SourceLocExpr *E) { + EvaluatedSourceLocScope LocScope = + CurSourceLocExprScope.getEvaluatedInfo(getContext(), E); + return MakeAddrLValue( + CGM.GetAddrOfConstantStringFromSourceLocExpr(E, LocScope), + LocScope.getType(), AlignmentSource::Decl); +} + LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) { auto SL = E->getFunctionName(); assert(SL != nullptr && "No StringLiteral name in PredefinedExpr"); @@ -3184,21 +3198,28 @@ Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, LValueBaseInfo *BaseInfo, TBAAAccessInfo *TBAAInfo) { - assert(E->getType()->isArrayType() && + LValue LV = EmitLValue(E); + return EmitArrayToPointerDecay(E, E->getType(), LV, BaseInfo, TBAAInfo); +} + +Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, QualType Ty, + LValue LV, + LValueBaseInfo *BaseInfo, + TBAAAccessInfo *TBAAInfo) { + assert(Ty->isArrayType() && "Array to pointer decay must have array source type!"); // Expressions of array type can't be bitfields or vector elements. - LValue LV = EmitLValue(E); Address Addr = LV.getAddress(); // If the array type was an incomplete type, we need to make sure // the decay ends up being the right type. - llvm::Type *NewTy = ConvertType(E->getType()); + llvm::Type *NewTy = ConvertType(Ty); Addr = Builder.CreateElementBitCast(Addr, NewTy); // Note that VLA pointers are always decayed, so we don't need to do // anything here. - if (!E->getType()->isVariableArrayType()) { + if (!Ty->isVariableArrayType()) { assert(isa(Addr.getElementType()) && "Expected pointer to array"); Addr = Builder.CreateStructGEP(Addr, 0, CharUnits::Zero(), "arraydecay"); @@ -3209,7 +3230,7 @@ // accesses to elements of member arrays, we conservatively represent accesses // to the pointee object as if it had no any base lvalue specified. // TODO: Support TBAA for member arrays. - QualType EltType = E->getType()->castAsArrayTypeUnsafe()->getElementType(); + QualType EltType = Ty->castAsArrayTypeUnsafe()->getElementType(); if (BaseInfo) *BaseInfo = LV.getBaseInfo(); if (TBAAInfo) *TBAAInfo = CGM.getTBAAAccessInfo(EltType); Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -162,10 +162,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); Index: lib/CodeGen/CGExprComplex.cpp =================================================================== --- lib/CodeGen/CGExprComplex.cpp +++ lib/CodeGen/CGExprComplex.cpp @@ -211,10 +211,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 @@ -1063,6 +1063,15 @@ return CGM.GetConstantArrayFromStringLiteral(E); } + llvm::Constant *VisitSourceLocExpr(SourceLocExpr *E, QualType T) { + T.dump(); + EvaluatedSourceLocScope LocScope = + Emitter.CGF->CurSourceLocExprScope.getEvaluatedInfo(CGM.getContext(), + E); + return llvm::ConstantDataArray::getString(VMContext, + LocScope.getStringValue(), false); + } + llvm::Constant *VisitObjCEncodeExpr(ObjCEncodeExpr *E, QualType T) { // This must be an @encode initializing an array in a static initializer. // Don't emit it as the address of the string, emit the string data itself @@ -1590,6 +1599,7 @@ ConstantLValue VisitStmt(const Stmt *S) { return nullptr; } ConstantLValue VisitCompoundLiteralExpr(const CompoundLiteralExpr *E); ConstantLValue VisitStringLiteral(const StringLiteral *E); + ConstantLValue VisitSourceLocExpr(const SourceLocExpr *E); ConstantLValue VisitObjCEncodeExpr(const ObjCEncodeExpr *E); ConstantLValue VisitObjCStringLiteral(const ObjCStringLiteral *E); ConstantLValue VisitPredefinedExpr(const PredefinedExpr *E); @@ -1753,6 +1763,16 @@ } ConstantLValue +ConstantLValueEmitter::VisitSourceLocExpr(const SourceLocExpr *E) { + if (auto CGF = Emitter.CGF) { + LValue Res = CGF->EmitSourceLocExprLValue(E); + return cast(Res.getAddress()); + } + auto LocScope = EvaluatedSourceLocScope::Create(CGM.getContext(), E, nullptr); + return CGM.GetAddrOfConstantStringFromSourceLocExpr(E, LocScope); +} + +ConstantLValue ConstantLValueEmitter::VisitObjCEncodeExpr(const ObjCEncodeExpr *E) { return CGM.GetAddrOfConstantStringFromObjCEncode(E); } Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -581,12 +581,30 @@ Value *VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) { return EmitLoadOfLValue(E); } + Value *VisitSourceLocExpr(SourceLocExpr *SLE) { + auto &Ctx = CGF.getContext(); + EvaluatedSourceLocScope LocScope = + CGF.CurSourceLocExprScope.getEvaluatedInfo(Ctx, SLE); + + if (SLE->isIntType()) + return Builder.getInt(LocScope.Result.getInt()); + + // else, we're building a string literal + LValue LV = CGF.EmitSourceLocExprLValue(SLE); + return CGF.EmitArrayToPointerDecay(SLE, LocScope.getType(), LV) + .getPointer(); + // return LV.getPointer(); + + StringLiteral *Str = LocScope.CreateStringLiteral(Ctx); + return CGF.EmitArrayToPointerDecay(Str).getPointer(); + } 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" @@ -1282,6 +1283,10 @@ SourceLocation LastStopPoint; public: + /// Source location information about the default argument or member + /// initializer expression we're evaluating, if any. + CurrentSourceLocExprScope CurSourceLocExprScope; + /// 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. @@ -1304,9 +1309,10 @@ /// is overridden to be the object under construction. class CXXDefaultInitExprScope { public: - CXXDefaultInitExprScope(CodeGenFunction &CGF) + CXXDefaultInitExprScope(CodeGenFunction &CGF, const CXXDefaultInitExpr *E) : CGF(CGF), OldCXXThisValue(CGF.CXXThisValue), - OldCXXThisAlignment(CGF.CXXThisAlignment) { + OldCXXThisAlignment(CGF.CXXThisAlignment), + SourceLocScope(E, CGF.CurSourceLocExprScope, CGF.CurCodeDecl) { CGF.CXXThisValue = CGF.CXXDefaultInitExprThis.getPointer(); CGF.CXXThisAlignment = CGF.CXXDefaultInitExprThis.getAlignment(); } @@ -1319,6 +1325,16 @@ CodeGenFunction &CGF; llvm::Value *OldCXXThisValue; CharUnits OldCXXThisAlignment; + SourceLocExprScopeGuard SourceLocScope; + }; + + /// The scope of a CXXDefaultArgExpr. + class CXXDefaultArgExprScope : public SourceLocExprScopeGuard { + using Base = SourceLocExprScopeGuard; + + public: + CXXDefaultArgExprScope(CodeGenFunction &CGF, const CXXDefaultArgExpr *E) + : Base(E, CGF.CurSourceLocExprScope, CGF.CurCodeDecl) {} }; /// The scope of an ArrayInitLoopExpr. Within this scope, the value of the @@ -3319,6 +3335,7 @@ LValue EmitStringLiteralLValue(const StringLiteral *E); LValue EmitObjCEncodeExprLValue(const ObjCEncodeExpr *E); LValue EmitPredefinedLValue(const PredefinedExpr *E); + LValue EmitSourceLocExprLValue(const SourceLocExpr *E); LValue EmitUnaryOpLValue(const UnaryOperator *E); LValue EmitArraySubscriptExpr(const ArraySubscriptExpr *E, bool Accessed = false); @@ -3341,6 +3358,9 @@ Address EmitArrayToPointerDecay(const Expr *Array, LValueBaseInfo *BaseInfo = nullptr, TBAAAccessInfo *TBAAInfo = nullptr); + Address EmitArrayToPointerDecay(const Expr *E, QualType Ty, LValue LV, + LValueBaseInfo *BaseInfo = nullptr, + TBAAAccessInfo *TBAAInfo = nullptr); class ConstantEmission { llvm::PointerIntPair ValueAndIsReference; Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -24,6 +24,7 @@ #include "clang/AST/DeclOpenMP.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/Mangle.h" +#include "clang/AST/SourceLocExprScope.h" #include "clang/Basic/ABI.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/Module.h" @@ -884,6 +885,11 @@ ConstantAddress GetAddrOfConstantStringFromObjCEncode(const ObjCEncodeExpr *); + /// Return a pointer to a constant array for the given SourceLocExpr node + /// when evaluated in the current environment. + ConstantAddress GetAddrOfConstantStringFromSourceLocExpr( + const SourceLocExpr *E, const EvaluatedSourceLocScope &LocInfo); + /// Returns a pointer to a character array containing the literal and a /// terminating '\0' character. The result has pointer to array type. /// Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -4148,6 +4148,19 @@ return GetAddrOfConstantCString(Str); } +ConstantAddress CodeGenModule::GetAddrOfConstantStringFromSourceLocExpr( + const SourceLocExpr *E, const EvaluatedSourceLocScope &LocInfo) { + std::string Str = LocInfo.getStringValue(); + StringRef GlobalName = [&]() -> StringRef { + if (Str.empty()) + return ".str.empty"; + if (E->getIdentType() == SourceLocExpr::File) + return ".str.file"; + return ".str.func"; + }(); + return GetAddrOfConstantCString(Str, GlobalName.data()); +} + /// GetAddrOfConstantCString - Returns a pointer to a character array containing /// the literal and a terminating '\0' character. /// The result has pointer to array type. Index: lib/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -641,6 +641,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 ']' @@ -1096,6 +1100,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()); @@ -2033,6 +2041,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: @@ -2252,6 +2264,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 @@ -12869,7 +12869,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()) @@ -12910,7 +12910,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 @@ -1268,6 +1268,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 @@ -4654,7 +4654,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 @@ -4916,8 +4916,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; @@ -13595,6 +13594,20 @@ 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(Context, Type, BuiltinLoc, RPLoc, 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); } /// 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); } /// Build a new C++ zero-initialization expression. @@ -2929,6 +2930,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::IdentType Type, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, + DeclContext *ParentContext) { + return getSema().BuildSourceLocExpr(Type, BuiltinLoc, RPLoc, ParentContext); + } + /// Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. @@ -9838,6 +9851,20 @@ } 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) { // Transform the callee. @@ -10072,8 +10099,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); @@ -10088,7 +10115,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 @@ -912,6 +912,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()); @@ -1425,12 +1433,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(); } @@ -3402,6 +3412,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 @@ -851,6 +851,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()); @@ -1394,6 +1403,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; } @@ -1401,6 +1411,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 @@ -1308,6 +1308,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 =================================================================== --- /dev/null +++ test/CodeGenCXX/builtin-source-location.cpp @@ -0,0 +1,167 @@ +// RUN: %clang_cc1 -std=c++2a -fblocks %s -triple %itanium_abi_triple -emit-llvm -o %s.ll +// RUN: FileCheck --input-file %s.ll %s --check-prefix=CHECK-BASIC-GLOBAL +// RUN: FileCheck --input-file %s.ll %s --check-prefix=CHECK-BASIC-LOCAL +// RUN: FileCheck --input-file %s.ll %s --check-prefix=CHECK-INIT +// | FileCheck %s -check-prefix= + +#line 4 "builtin-source-location.cpp" + +extern "C" { +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; + } + constexpr source_location() = default; + constexpr source_location(source_location const &) = default; + constexpr unsigned int line() const noexcept { return __m_line; } + constexpr unsigned int column() const noexcept { return __m_col; } + constexpr const char *file() const noexcept { return __m_file; } + constexpr const char *function() const noexcept { return __m_func; } +}; +} +constexpr 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.set(__line, __col, __file, __func); + return __loc; +} +source_location bad_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.set(__line, __col, __file, __func); + return __loc; +} + +using SL = source_location; + +template +T launder(T val) { return val; } +extern "C" void sink(...); + +constexpr SL forward(SL sl = current()) { + return sl; +} +SL forward_bad(SL sl = bad_current()) { + return sl; +} + +// CHECK-BASIC-GLOBAL: @[[CUR_FILE:.+]] = private unnamed_addr constant [28 x i8] c"builtin-source-location.cpp\00", align 1 +// +// CHECK-BASIC-GLOBAL: @basic_global = global %struct.source_location { +// CHECK-BASIC-GLOBAL-SAME: i32 1000, i32 {{[0-9]+}}, +// CHECK-BASIC-GLOBAL-SAME: {{.*}} i8* getelementptr inbounds ({{.*}}@[[CUR_FILE]], +// CHECK-BASIC-GLOBAL-SAME: {{.*}} i8* getelementptr inbounds ({{.*}}@.str.empty, +// +#line 1000 "test_basic.cpp" +SL basic_global = current(); + +// CHECK-BASIC-GLOBAL-DAG: @basic_global_bad = global %struct.source_location zeroinitializer, align 8 +// CHECK-BASIC-GLOBAL-DAG: @[[BAD_FILE:.*]] = private unnamed_addr constant {{.*}}c"test_basic.cpp\00", align 1 +// CHECK-BASIC-GLOBAL: define internal void @__cxx_global_var_init +// CHECK-BASIC-GLOBAL-NEXT: entry: +// CHECK-BASIC-GLOBAL-NEXT: call void @_Z11bad_currentPKcS0_jj(%struct.source_location* sret @basic_global_bad, +// CHECK-BASIC-GLOBAL-SAME: {{.*}} i8* getelementptr inbounds ({{.*}}@[[BAD_FILE]], +// CHECK-BASIC-GLOBAL-SAME: {{.*}} i8* getelementptr inbounds ({{.*}}@.str.empty, +// CHECK-BASIC-GLOBAL-SAME: {{.*}}), i32 1004, i32 {{[0-9]+}} +// CHECK-BASIC-GLOBAL-NEXT: ret void +#line 1100 "test_basic_bad.cpp" +extern "C" void before_use_globals() {} +SL basic_global_bad = bad_current(); + +extern "C" void basic_use_globals() { + sink(basic_global); + sink(basic_global_bad); +} + +#line 2000 "test_function.cpp" +extern "C" void test_function() { +#line 2100 "test_current.cpp" + SL local = current(); +#line 2200 "test_bad_current.cpp" + SL bad_local = bad_current(); +#line 2300 "test_over.cpp" + sink(local); + sink(bad_local); +} + +#line 3000 "test_default_init.cpp" +extern "C" struct TestInit { + SL info = current(); + SL bad_info = bad_current(); + SL nested_info = forward(); + SL arg_info; + SL bad_arg_info; + +#line 3100 "default.cpp" + TestInit() = default; +#line 3200 "TestInit.cpp" + TestInit(int, SL arg_info = current(), + SL bad_arg_info = bad_current()) : arg_info(arg_info), bad_arg_info(bad_arg_info) {} +}; +#line 3300 "GlobalInitDefault.cpp" +TestInit GlobalInitDefault; +TestInit GlobalInitVal(42); +extern "C" void test_init_function() { +#line 3400 "LocalInitDefault.cpp" + TestInit InitDefault; + TestInit InitVal(42); + sink(GlobalInitDefault); + sink(GlobalInitVal); + sink(InitDefault); + sink(InitVal); +} + +#line 4000 "test_init_constexpr.cpp" +extern "C" struct TestInitConstexpr { + SL info = current(); + SL arg_info; +#line 4100 "TestInitConstexpr" + TestInitConstexpr() = default; + constexpr TestInitConstexpr(int, SL arg_info = current()) : arg_info(arg_info) {} +}; +#line 4200 "TestInitConstexprGlobal.cpp" +TestInitConstexpr GlobalInitDefaultConstexpr; +TestInitConstexpr GlobalInitValConstexpr(42); +extern "C" void test_init_function_constexpr() { +#line 4300 "TestInitConstexprFunc.cpp" + TestInitConstexpr InitDefaultConstexpr; + TestInit InitValConstexpr(42); + sink(GlobalInitDefaultConstexpr); + sink(GlobalInitValConstexpr); + sink(InitDefaultConstexpr); + sink(InitValConstexpr); +} + +#line 5000 "test_init_agg.cpp" +extern "C" struct TestInitAgg { + SL info = current(); +#line 5100 "TestInitAgg.cpp" +}; +#line 5200 "TestInitAggGlobal.cpp" +TestInitAgg GlobalInitDefaultAgg; +TestInitAgg GlobalInitValAgg = {}; +extern "C" void test_init_function_agg() { +#line 5300 "TestInitAggFunc.cpp" + TestInitAgg InitDefaultAgg; + TestInitAgg InitValAgg = {}; + sink(GlobalInitDefaultAgg); + sink(GlobalInitValAgg); + sink(InitDefaultAgg); + sink(InitValAgg); +} Index: test/CodeGenCXX/builtin_FUNCTION.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/builtin_FUNCTION.cpp @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -std=c++2a -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 + +namespace test_default_init { +struct InInit { + const char *f = test_func::test_default_arg(); + const char *f2 = nullptr; + + InInit() = default; + + constexpr InInit(int, const char *ff = __builtin_FUNCTION()) + : f2(ff) {} +}; +InInit Default; +InInit Conv(42); +InInit Brace = {}; + +} // namespace test_default_init 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,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 =================================================================== --- /dev/null +++ test/SemaCXX/source_location.cpp @@ -0,0 +1,564 @@ +// 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 313 + 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